@milaboratories/milaboratories.ui-examples 1.4.0 → 1.4.1

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/index.js CHANGED
@@ -4453,13 +4453,6 @@ objectType({
4453
4453
  function assertNever(x) {
4454
4454
  throw new Error("Unexpected object: " + x);
4455
4455
  }
4456
- /**
4457
- * Return unique entries of the array by the provided id
4458
- * For each id, the last entry is kept
4459
- */
4460
- function uniqueBy(array, makeId) {
4461
- return [...new Map(array.map((e) => [makeId(e), e])).values()];
4462
- }
4463
4456
  //#endregion
4464
4457
  //#region ../../../../lib/model/common/dist/drivers/pframe/data_info.js
4465
4458
  /**
@@ -4934,7 +4927,7 @@ function hasCycleOfParents(axisSpec) {
4934
4927
  }
4935
4928
  /** Create list of normalized axisSpec (parents are in array of specs, not indexes) */
4936
4929
  function getNormalizedAxesList(axes) {
4937
- if (!axes.length) return [];
4930
+ if (axes.length === 0) return [];
4938
4931
  const modifiedAxes = axes.map((axis) => {
4939
4932
  const { parentAxes: _, ...copiedRest } = axis;
4940
4933
  return {
@@ -5074,20 +5067,23 @@ function traverseQuerySpec(query, visitor) {
5074
5067
  secondary: query.secondary.map(traverseEntry)
5075
5068
  };
5076
5069
  break;
5077
- case "linkerJoin":
5070
+ case "linkerJoin": {
5071
+ const linker = "type" in query.linker ? query.linker : {
5072
+ type: "column",
5073
+ column: query.linker.column
5074
+ };
5078
5075
  result = {
5079
5076
  ...query,
5080
- linker: {
5081
- ...query.linker,
5082
- column: visitor.column(query.linker.column)
5083
- },
5077
+ linker: traverseQuerySpec(linker, visitor),
5084
5078
  secondary: query.secondary.map(traverseEntry)
5085
5079
  };
5086
5080
  break;
5081
+ }
5087
5082
  case "filter":
5088
5083
  case "sort":
5089
5084
  case "sliceAxes":
5090
5085
  case "transformColumns":
5086
+ case "specOverride":
5091
5087
  result = {
5092
5088
  ...query,
5093
5089
  input: traverseQuerySpec(query.input, visitor)
@@ -5098,8 +5094,8 @@ function traverseQuerySpec(query, visitor) {
5098
5094
  return visitor.node ? visitor.node(result) : result;
5099
5095
  }
5100
5096
  /** Recursively maps all column references in a SpecQuery tree. */
5101
- function mapSpecQueryColumns(query, cb) {
5102
- return traverseQuerySpec(query, { column: cb });
5097
+ function mapSpecQueryColumns(query, visitor) {
5098
+ return traverseQuerySpec(query, visitor);
5103
5099
  }
5104
5100
  //#endregion
5105
5101
  //#region ../../../../lib/model/common/dist/drivers/pframe/table_calculate.js
@@ -5109,8 +5105,8 @@ function mapPTableDef(def, cb) {
5109
5105
  src: mapJoinEntry(def.src, cb)
5110
5106
  };
5111
5107
  }
5112
- function mapPTableDefV2(def, cb) {
5113
- return { query: mapSpecQueryColumns(def.query, cb) };
5108
+ function mapPTableDefV2(def, visitor) {
5109
+ return { query: mapSpecQueryColumns(def.query, visitor) };
5114
5110
  }
5115
5111
  function mapJoinEntry(entry, cb) {
5116
5112
  switch (entry.type) {
@@ -5159,14 +5155,325 @@ function getPTableColumnId(spec) {
5159
5155
  }
5160
5156
  }
5161
5157
  //#endregion
5158
+ //#region ../../../../lib/model/common/dist/drivers/pframe/spec/filtered_column.js
5159
+ function stringifyColumnFilteredId(key) {
5160
+ return canonicalizeJson(createColumnFilteredKey(key));
5161
+ }
5162
+ function isColumnFilteredKey(obj) {
5163
+ return typeof obj === "object" && obj !== null && "__isFiltered" in obj;
5164
+ }
5165
+ function createColumnFilteredKey(props) {
5166
+ const axisFilters = props.axisFilters.toSorted((a, b) => a[0] - b[0]);
5167
+ return {
5168
+ __isFiltered: true,
5169
+ source: props.source,
5170
+ axisFilters
5171
+ };
5172
+ }
5173
+ /**
5174
+ * Drop the axes pinned by `axisFilters` from a {@link PColumnSpec}'s
5175
+ * `axesSpec`. Indices are positional against `spec.axesSpec`. Returns `spec`
5176
+ * unchanged when there are no filters.
5177
+ *
5178
+ * Single source of the Filtered-layer spec math — shared by
5179
+ * `reconstructSpecFromId` (host, id-walking) and `ColumnFilteredRecipe.getSpec`
5180
+ * (sandbox, recipe-graph walking).
5181
+ */
5182
+ function applyAxisFilters(spec, axisFilters) {
5183
+ if (axisFilters.length === 0) return spec;
5184
+ const fixed = new Set(axisFilters.map(([i]) => i));
5185
+ return {
5186
+ ...spec,
5187
+ axesSpec: spec.axesSpec.filter((_, i) => !fixed.has(i))
5188
+ };
5189
+ }
5190
+ //#endregion
5191
+ //#region ../../../../lib/model/common/dist/pool/spec.js
5192
+ function isPObjectId(value) {
5193
+ if (typeof value !== "string") return false;
5194
+ return isPObjectKey(parseJsonSafely(value));
5195
+ }
5196
+ function isPObjectKey(value) {
5197
+ return isLocalPObjectKey(value) || isGlobalPObjectKey(value);
5198
+ }
5199
+ function createPObjectId(obj) {
5200
+ if (isLocalPObjectKey(obj)) return createLocalPObjectId(obj.resolvePath, obj.name);
5201
+ if (isGlobalPObjectKey(obj)) return createGlobalPObjectId(obj.blockId, obj.name);
5202
+ throw new Error(`createPObjectId: unrecognized object key structure: ${JSON.stringify(obj)}`);
5203
+ }
5204
+ function createLocalPObjectId(resolvePath, name) {
5205
+ return canonicalizeJson({
5206
+ resolvePath,
5207
+ name
5208
+ });
5209
+ }
5210
+ function isLocalPObjectKey(value) {
5211
+ if (typeof value !== "object" || value === null) return false;
5212
+ const v = value;
5213
+ return Array.isArray(v.resolvePath) && typeof v.name === "string";
5214
+ }
5215
+ function createGlobalPObjectId(blockId, exportName) {
5216
+ return canonicalizeJson({
5217
+ __isRef: true,
5218
+ blockId,
5219
+ name: exportName
5220
+ });
5221
+ }
5222
+ function isGlobalPObjectKey(value) {
5223
+ if (typeof value !== "object" || value === null) return false;
5224
+ return "__isRef" in value && "blockId" in value && "name" in value;
5225
+ }
5226
+ function isPObject(obj) {
5227
+ return typeof obj === "object" && obj !== null && "id" in obj && "spec" in obj && "data" in obj;
5228
+ }
5229
+ function isPColumn(obj) {
5230
+ return isPObject(obj) && isPColumnSpec(obj.spec);
5231
+ }
5232
+ function isPColumnSpec(spec) {
5233
+ return spec.kind === "PColumn";
5234
+ }
5235
+ function ensurePColumn(obj) {
5236
+ if (!isPColumn(obj)) throw new Error(`not a PColumn (kind = ${obj.spec.kind})`);
5237
+ return obj;
5238
+ }
5239
+ function mapPObjectData(pObj, cb) {
5240
+ return pObj === void 0 ? void 0 : {
5241
+ ...pObj,
5242
+ data: cb(pObj.data)
5243
+ };
5244
+ }
5245
+ function extractAllColumns(entry) {
5246
+ const columns = /* @__PURE__ */ new Map();
5247
+ const addAllColumns = (entry) => {
5248
+ switch (entry.type) {
5249
+ case "column":
5250
+ columns.set(entry.column.id, entry.column);
5251
+ return;
5252
+ case "slicedColumn":
5253
+ columns.set(entry.column.id, entry.column);
5254
+ return;
5255
+ case "artificialColumn":
5256
+ columns.set(entry.column.id, entry.column);
5257
+ return;
5258
+ case "inlineColumn": return;
5259
+ case "full":
5260
+ case "inner":
5261
+ for (const e of entry.entries) addAllColumns(e);
5262
+ return;
5263
+ case "outer":
5264
+ addAllColumns(entry.primary);
5265
+ for (const e of entry.secondary) addAllColumns(e);
5266
+ return;
5267
+ default: assertNever(entry);
5268
+ }
5269
+ };
5270
+ addAllColumns(entry);
5271
+ return [...columns.values()];
5272
+ }
5273
+ //#endregion
5274
+ //#region ../../../../lib/util/helpers/dist/utils.js
5275
+ function isNil$1(v) {
5276
+ return v === null || v === void 0;
5277
+ }
5278
+ Array.isArray;
5279
+ //#endregion
5280
+ //#region ../../../../lib/util/helpers/dist/error.js
5281
+ function throwError(v) {
5282
+ if (typeof v === "string") throw new Error(v);
5283
+ else throw v;
5284
+ }
5285
+ //#endregion
5286
+ //#region ../../../../lib/model/common/dist/drivers/pframe/spec/discovered_column.js
5287
+ function isColumnDiscoveredKey(obj) {
5288
+ return typeof obj === "object" && obj !== null && "__isDiscovered" in obj;
5289
+ }
5290
+ function distillColumnDiscoveredKey(props) {
5291
+ return {
5292
+ __isDiscovered: true,
5293
+ column: props.column,
5294
+ path: Array.isArray(props.path) && props.path.length > 0 ? props.path : void 0,
5295
+ columnQualifications: Array.isArray(props.columnQualifications) && props.columnQualifications.length > 0 ? props.columnQualifications : void 0,
5296
+ queriesQualifications: props.queriesQualifications && Object.keys(props.queriesQualifications).length > 0 ? props.queriesQualifications : void 0
5297
+ };
5298
+ }
5299
+ function stringifyColumnDiscoveredId(id) {
5300
+ return canonicalizeJson(distillColumnDiscoveredKey({
5301
+ __isDiscovered: true,
5302
+ ...id
5303
+ }));
5304
+ }
5305
+ //#endregion
5306
+ //#region ../../../../node_modules/.pnpm/es-toolkit@1.39.10/node_modules/es-toolkit/dist/function/identity.mjs
5307
+ function identity(x) {
5308
+ return x;
5309
+ }
5310
+ //#endregion
5311
+ //#region ../../../../node_modules/.pnpm/es-toolkit@1.39.10/node_modules/es-toolkit/dist/predicate/isFunction.mjs
5312
+ function isFunction(value) {
5313
+ return typeof value === "function";
5314
+ }
5315
+ //#endregion
5316
+ //#region ../../../../node_modules/.pnpm/es-toolkit@1.39.10/node_modules/es-toolkit/dist/predicate/isNil.mjs
5317
+ function isNil(x) {
5318
+ return x == null;
5319
+ }
5320
+ //#endregion
5321
+ //#region ../../../../node_modules/.pnpm/es-toolkit@1.39.10/node_modules/es-toolkit/dist/predicate/isString.mjs
5322
+ function isString(value) {
5323
+ return typeof value === "string";
5324
+ }
5325
+ //#endregion
5162
5326
  //#region ../../../../lib/model/common/dist/drivers/pframe/spec/ids.js
5163
5327
  /**
5164
- * Canonically serializes a {@link UniversalPColumnId} to a string.
5165
- * @param id - The column identifier to serialize
5166
- * @returns The canonically serialized string
5328
+ * Canonically serializes a column key to a branded string id. Accepts both
5329
+ * the new {@link ColumnUniversalKey} and the deprecated {@link UniversalPColumnId}
5330
+ * (anchored / old filtered object form).
5167
5331
  */
5168
5332
  function stringifyColumnId(id) {
5169
- return (0, import_canonicalize.default)(id);
5333
+ return canonicalizeJson(id);
5334
+ }
5335
+ function parseColumnIdSafely(str, fallback = void 0) {
5336
+ try {
5337
+ return JSON.parse(str);
5338
+ } catch {
5339
+ return fallback;
5340
+ }
5341
+ }
5342
+ /**
5343
+ * Walk a rich column id down to its terminal leaf {@link PObjectId}.
5344
+ */
5345
+ function extractPObjectId(id) {
5346
+ if (isString(id)) {
5347
+ if (isPObjectId(id)) return id;
5348
+ return extractPObjectId(parseColumnIdSafely(id) ?? throwError(`extractPObjectId: id "${id}" is not a valid canonical column id`));
5349
+ }
5350
+ if (isPObjectKey(id)) return createPObjectId(id);
5351
+ if (isColumnFilteredKey(id)) return extractPObjectId(id.source);
5352
+ if (isColumnOverriddenKey(id)) return extractPObjectId(id.source);
5353
+ if (isColumnDiscoveredKey(id)) return extractPObjectId(id.column);
5354
+ throw new Error(`extractPObjectId: unrecognized column id structure: ${JSON.stringify(id)}`);
5355
+ }
5356
+ //#endregion
5357
+ //#region ../../../../lib/model/common/dist/drivers/pframe/spec/overridden.js
5358
+ function isColumnOverriddenKey(obj) {
5359
+ return typeof obj === "object" && obj !== null && "__isOverridden" in obj;
5360
+ }
5361
+ function distillColumnOverriddenKey(props) {
5362
+ return {
5363
+ __isOverridden: true,
5364
+ source: props.source,
5365
+ specOverrides: props.specOverrides
5366
+ };
5367
+ }
5368
+ function createColumnOverriddenId(props) {
5369
+ return stringifyColumnOverriddenId(createColumnOverriddenKey(props));
5370
+ }
5371
+ function createColumnOverriddenKey(props) {
5372
+ const { source, specOverrides } = props;
5373
+ const unwrapped = unwrapOverrides(source);
5374
+ return {
5375
+ __isOverridden: true,
5376
+ source: unwrapped ? unwrapped.source : source,
5377
+ specOverrides: unwrapped ? mergeSpecOverrides(unwrapped.specOverrides, specOverrides) : specOverrides
5378
+ };
5379
+ }
5380
+ function stringifyColumnOverriddenId(id) {
5381
+ return canonicalizeJson(distillColumnOverriddenKey(id));
5382
+ }
5383
+ /**
5384
+ * Peel one override-wrap layer.
5385
+ *
5386
+ * Invariant: `{source, specOverrides}` can only appear at the top level —
5387
+ * the inner `source` is never itself an override-wrap. Anything else throws.
5388
+ */
5389
+ function unwrapOverrides(id) {
5390
+ const parsed = parseColumnIdSafely(id);
5391
+ if (parsed === void 0 || !isColumnOverriddenKey(parsed)) return void 0;
5392
+ const inner = parseColumnIdSafely(parsed.source);
5393
+ if (inner !== void 0 && isColumnOverriddenKey(inner)) throw new Error("nested override-wrap detected — invariant broken");
5394
+ return parsed;
5395
+ }
5396
+ /**
5397
+ * Apply `specOverrides` from a rich id (`SUniversalPColumnId`) on top of a
5398
+ * resolved {@link PColumnSpec}. Returns `base` unchanged when the id carries
5399
+ * no overrides.
5400
+ */
5401
+ function applySpecOverrides(base, idOrOverride) {
5402
+ if (isNil(idOrOverride)) return base;
5403
+ const overrides = isString(idOrOverride) ? unwrapOverrides(idOrOverride)?.specOverrides : idOrOverride;
5404
+ if (isNil(overrides)) return base;
5405
+ const result = { ...base };
5406
+ if (overrides.annotations) result.annotations = mergeRecord(base.annotations, overrides.annotations);
5407
+ if (overrides.domain) result.domain = mergeRecord(base.domain, overrides.domain);
5408
+ if (overrides.contextDomain) result.contextDomain = mergeRecord(base.contextDomain, overrides.contextDomain);
5409
+ if (overrides.axesSpec) result.axesSpec = applyAxesPatches(base.axesSpec, overrides.axesSpec);
5410
+ return result;
5411
+ }
5412
+ /**
5413
+ * Compose two override patches: applying `mergeSpecOverrides(prior, next)` on
5414
+ * top of a base spec produces the same result as applying `prior` then `next`.
5415
+ */
5416
+ function mergeSpecOverrides(prior, next) {
5417
+ const result = {};
5418
+ const axesSpec = mergeAxesPatches(prior.axesSpec, next.axesSpec);
5419
+ if (axesSpec !== void 0) result.axesSpec = axesSpec;
5420
+ const annotations = mergeRecord(prior.annotations, next.annotations);
5421
+ if (annotations !== void 0) result.annotations = annotations;
5422
+ const domain = mergeRecord(prior.domain, next.domain);
5423
+ if (domain !== void 0) result.domain = domain;
5424
+ const contextDomain = mergeRecord(prior.contextDomain, next.contextDomain);
5425
+ if (contextDomain !== void 0) result.contextDomain = contextDomain;
5426
+ return result;
5427
+ }
5428
+ /**
5429
+ * Apply positional `patches` onto `base.axesSpec`. Patches deep-merge
5430
+ * `annotations` / `domain` / `contextDomain` and shallow-override `name` /
5431
+ * `type` at their slot. Indices `>= base.length` append new axes; intermediate
5432
+ * gaps (if any) are filled with the patch itself, so callers should not leave
5433
+ * holes between base.length and the highest patched index.
5434
+ */
5435
+ function applyAxesPatches(base, patches) {
5436
+ const result = base.slice();
5437
+ for (const [k, p] of Object.entries(patches)) {
5438
+ const i = Number(k);
5439
+ const existing = result[i];
5440
+ result[i] = existing === void 0 ? p : mergeAxisPatch(existing, p);
5441
+ }
5442
+ return result;
5443
+ }
5444
+ /**
5445
+ * Compose two positional patch maps: `mergeAxesPatches(prior, next)` applied to
5446
+ * a base spec must equal applying `prior` then `next`. Patches sharing an
5447
+ * index deep-merge field-by-field.
5448
+ */
5449
+ function mergeAxesPatches(prior, next) {
5450
+ if (!prior || Object.keys(prior).length === 0) return next;
5451
+ if (!next || Object.keys(next).length === 0) return prior;
5452
+ const result = { ...prior };
5453
+ for (const [k, p] of Object.entries(next)) {
5454
+ const i = Number(k);
5455
+ const existing = result[i];
5456
+ result[i] = existing === void 0 ? p : mergeAxisPatch(existing, p);
5457
+ }
5458
+ return result;
5459
+ }
5460
+ function mergeAxisPatch(axis, patch) {
5461
+ const result = {
5462
+ ...axis,
5463
+ ...patch
5464
+ };
5465
+ if (patch.annotations) result.annotations = mergeRecord(axis.annotations, patch.annotations);
5466
+ if (patch.domain) result.domain = mergeRecord(axis.domain, patch.domain);
5467
+ if (patch.contextDomain) result.contextDomain = mergeRecord(axis.contextDomain, patch.contextDomain);
5468
+ return result;
5469
+ }
5470
+ function mergeRecord(a, b) {
5471
+ if (!b) return a;
5472
+ if (!a) return b;
5473
+ return {
5474
+ ...a,
5475
+ ...b
5476
+ };
5170
5477
  }
5171
5478
  //#endregion
5172
5479
  //#region ../../../../lib/model/common/dist/drivers/pframe/spec/anchored.js
@@ -5415,52 +5722,6 @@ function isAnchorAxisRef(value) {
5415
5722
  return typeof value === "object" && "anchor" in value;
5416
5723
  }
5417
5724
  //#endregion
5418
- //#region ../../../../lib/model/common/dist/pool/spec.js
5419
- function isPColumnSpec(spec) {
5420
- return spec.kind === "PColumn";
5421
- }
5422
- function isPColumn(obj) {
5423
- return isPColumnSpec(obj.spec);
5424
- }
5425
- function ensurePColumn(obj) {
5426
- if (!isPColumn(obj)) throw new Error(`not a PColumn (kind = ${obj.spec.kind})`);
5427
- return obj;
5428
- }
5429
- function mapPObjectData(pObj, cb) {
5430
- return pObj === void 0 ? void 0 : {
5431
- ...pObj,
5432
- data: cb(typeof pObj.data === "function" ? pObj.data() : pObj.data)
5433
- };
5434
- }
5435
- function extractAllColumns(entry) {
5436
- const columns = /* @__PURE__ */ new Map();
5437
- const addAllColumns = (entry) => {
5438
- switch (entry.type) {
5439
- case "column":
5440
- columns.set(entry.column.id, entry.column);
5441
- return;
5442
- case "slicedColumn":
5443
- columns.set(entry.column.id, entry.column);
5444
- return;
5445
- case "artificialColumn":
5446
- columns.set(entry.column.id, entry.column);
5447
- return;
5448
- case "inlineColumn": return;
5449
- case "full":
5450
- case "inner":
5451
- for (const e of entry.entries) addAllColumns(e);
5452
- return;
5453
- case "outer":
5454
- addAllColumns(entry.primary);
5455
- for (const e of entry.secondary) addAllColumns(e);
5456
- return;
5457
- default: assertNever(entry);
5458
- }
5459
- };
5460
- addAllColumns(entry);
5461
- return [...columns.values()];
5462
- }
5463
- //#endregion
5464
5725
  //#region ../../../../lib/model/common/dist/drivers/pframe/spec/selectors.js
5465
5726
  /**
5466
5727
  * Determines if an axis ID matches an axis selector.
@@ -5554,18 +5815,6 @@ function deriveNativeId(spec) {
5554
5815
  return (0, import_canonicalize.default)(result);
5555
5816
  }
5556
5817
  //#endregion
5557
- //#region ../../../../lib/util/helpers/dist/utils.js
5558
- function isNil$1(v) {
5559
- return v === null || v === void 0;
5560
- }
5561
- Array.isArray;
5562
- //#endregion
5563
- //#region ../../../../lib/util/helpers/dist/error.js
5564
- function throwError(v) {
5565
- if (typeof v === "string") throw new Error(v);
5566
- else throw v;
5567
- }
5568
- //#endregion
5569
5818
  //#region ../../../../lib/model/common/dist/drivers/pframe/linker_columns.js
5570
5819
  var LinkerMap = class LinkerMap {
5571
5820
  /** Graph of linkers connected by axes (single or grouped by parents) */
@@ -5795,6 +6044,50 @@ function mapValueInVOE(voe, cb) {
5795
6044
  } : voe;
5796
6045
  }
5797
6046
  //#endregion
6047
+ //#region ../../../../lib/model/common/dist/resource_types.js
6048
+ /** Well-known resource type names used across the platform. */
6049
+ const ResourceTypeName = {
6050
+ StreamManager: "StreamManager",
6051
+ StdMap: "StdMap",
6052
+ StdMapSlash: "std/map",
6053
+ EphStdMap: "EphStdMap",
6054
+ PFrame: "PFrame",
6055
+ ParquetChunk: "ParquetChunk",
6056
+ BContext: "BContext",
6057
+ BlockPackCustom: "BlockPackCustom",
6058
+ BinaryMap: "BinaryMap",
6059
+ BinaryValue: "BinaryValue",
6060
+ BlobMap: "BlobMap",
6061
+ BResolveSingle: "BResolveSingle",
6062
+ BResolveSingleNoResult: "BResolveSingleNoResult",
6063
+ BQueryResult: "BQueryResult",
6064
+ TengoTemplate: "TengoTemplate",
6065
+ TengoLib: "TengoLib",
6066
+ SoftwareInfo: "SoftwareInfo",
6067
+ Dummy: "Dummy",
6068
+ JsonResourceError: "json/resourceError",
6069
+ JsonObject: "json/object",
6070
+ JsonGzObject: "json-gz/object",
6071
+ JsonString: "json/string",
6072
+ JsonArray: "json/array",
6073
+ JsonNumber: "json/number",
6074
+ BContextEnd: "BContextEnd",
6075
+ FrontendFromUrl: "Frontend/FromUrl",
6076
+ FrontendFromFolder: "Frontend/FromFolder",
6077
+ BObjectSpec: "BObjectSpec",
6078
+ Blob: "Blob",
6079
+ Null: "Null",
6080
+ Binary: "binary",
6081
+ LSProvider: "LSProvider",
6082
+ WorkingDirectory: "WorkingDirectory",
6083
+ UserProject: "UserProject",
6084
+ Projects: "Projects",
6085
+ ClientRoot: "ClientRoot",
6086
+ SharingOutbox: "SharingOutbox",
6087
+ SharingState: "SharingState",
6088
+ SharedEnvelope: "SharedEnvelope"
6089
+ };
6090
+ //#endregion
5798
6091
  //#region ../../../../lib/model/common/dist/services/service_types.js
5799
6092
  const SERVICE_ID_PATTERN = /^[a-zA-Z][a-zA-Z0-9]*$/;
5800
6093
  const { service, isNodeService, isWasmService, isMainService, getServiceKind, getServiceModelMethods, getServiceUiMethods } = (() => {
@@ -5892,6 +6185,28 @@ const Services = {
5892
6185
  name: "dialog",
5893
6186
  modelMethods: [],
5894
6187
  uiMethods: ["showSaveDialog"]
6188
+ }),
6189
+ ColumnsCollection: service()({
6190
+ type: "wasm",
6191
+ name: "columnsCollection",
6192
+ modelMethods: [
6193
+ "create",
6194
+ "isEmpty",
6195
+ "isFinal",
6196
+ "getColumns",
6197
+ "addSource",
6198
+ "discover",
6199
+ "filter"
6200
+ ],
6201
+ uiMethods: [
6202
+ "create",
6203
+ "isEmpty",
6204
+ "isFinal",
6205
+ "getColumns",
6206
+ "addSource",
6207
+ "discover",
6208
+ "filter"
6209
+ ]
5895
6210
  })
5896
6211
  };
5897
6212
  Object.keys(Services).map((key) => `requires${key}`);
@@ -5915,26 +6230,228 @@ function buildMethodMap(pick) {
5915
6230
  buildMethodMap(getServiceUiMethods);
5916
6231
  buildMethodMap(getServiceModelMethods);
5917
6232
  //#endregion
5918
- //#region ../../../../sdk/model/dist/render/accessor.js
5919
- function ifDef(value, cb) {
5920
- return value === void 0 ? void 0 : cb(value);
6233
+ //#region ../../../../lib/model/common/dist/columns/accessor_traversal.js
6234
+ /** Resource types that hold column collections — DFS stops here and collects. */
6235
+ const COLLECT_TYPES = [ResourceTypeName.PFrame];
6236
+ /** Resource types DFS descends through when looking for PFrames. */
6237
+ const DESCEND_TYPES = [ResourceTypeName.StdMap, ResourceTypeName.StdMapSlash];
6238
+ /**
6239
+ * Enumerate column names backing a PFrame accessor — derived from
6240
+ * `<name>.spec` field names, without resolving the spec resources.
6241
+ */
6242
+ function listColumnNames(accessor, prefix = "") {
6243
+ if (accessor.resourceType.name !== ResourceTypeName.PFrame) return [];
6244
+ const out = [];
6245
+ for (const field of accessor.listInputFields()) {
6246
+ if (!field.endsWith(".spec")) continue;
6247
+ const raw = field.slice(0, -5);
6248
+ if (!raw.startsWith(prefix)) continue;
6249
+ out.push(raw.slice(prefix.length));
6250
+ }
6251
+ return out;
5921
6252
  }
5922
6253
  /**
5923
- * Decode an error node's content into a display message. The backend serializes
5924
- * a resource error as `{"message": "..."}` (`ResourceError`); unwrap that to the
5925
- * human-readable message. Falls back to the raw string when the content is not
5926
- * that envelope (e.g. plain text, or an unexpected shape).
6254
+ * DFS over the input-field subtree of `root`. Collects nodes whose resource
6255
+ * type `collectTypes`. Descends only through `descendTypes`
6256
+ * (default: StdMap / StdMapSlash). Collected nodes are not descended into.
6257
+ *
6258
+ * Threads the field-name path from `rootPath` to each hit, so callers can
6259
+ * build canonical {@link createLocalPObjectId}s without relying on the
6260
+ * accessor itself to remember its path.
6261
+ *
6262
+ * Includes `root` itself in the walk.
5927
6263
  */
5928
- function decodeErrorMessage(raw) {
5929
- try {
5930
- const parsed = JSON.parse(raw);
5931
- if (typeof parsed?.message === "string") return parsed.message;
5932
- } catch {}
5933
- return raw;
6264
+ function findDescendantsByType(opts) {
6265
+ const { root, rootPath, collectTypes, descendTypes = DESCEND_TYPES } = opts;
6266
+ const collectSet = new Set(collectTypes);
6267
+ const descendSet = new Set(descendTypes);
6268
+ const result = [];
6269
+ const stack = [{
6270
+ node: root,
6271
+ path: rootPath
6272
+ }];
6273
+ while (stack.length > 0) {
6274
+ const { node, path } = stack.pop();
6275
+ const typeName = node.resourceType.name;
6276
+ if (collectSet.has(typeName)) {
6277
+ result.push({
6278
+ node,
6279
+ path
6280
+ });
6281
+ continue;
6282
+ }
6283
+ if (!descendSet.has(typeName)) continue;
6284
+ const fields = node.listInputFields();
6285
+ for (let i = fields.length - 1; i >= 0; i--) {
6286
+ const child = node.traverse({
6287
+ field: fields[i],
6288
+ assertFieldType: "Input",
6289
+ ignoreError: true
6290
+ });
6291
+ if (child !== void 0) stack.push({
6292
+ node: child,
6293
+ path: [...path, fields[i]]
6294
+ });
6295
+ }
6296
+ }
6297
+ return result;
5934
6298
  }
5935
- /** Represent resource tree node accessor */
5936
- var TreeNodeAccessor = class TreeNodeAccessor {
5937
- handle;
6299
+ /**
6300
+ * Walk an accessor root and return one {@link LeafEntry} per discovered column.
6301
+ * Ids are {@link createLocalPObjectId}-shaped: `{resolvePath, name}`. The
6302
+ * `resolvePath` is derived from `rootPath` extended by the DFS traversal.
6303
+ */
6304
+ function indexAccessorRoot(root, rootPath) {
6305
+ const result = [];
6306
+ for (const { node, path } of findDescendantsByType({
6307
+ root,
6308
+ rootPath,
6309
+ collectTypes: COLLECT_TYPES,
6310
+ descendTypes: DESCEND_TYPES
6311
+ })) for (const name of listColumnNames(node)) result.push({
6312
+ accessor: node,
6313
+ name,
6314
+ id: createLocalPObjectId([...path], name)
6315
+ });
6316
+ return result;
6317
+ }
6318
+ /**
6319
+ * Walk one upstream-block ctx pair (`prodCtx` then `stagingCtx`) and return one
6320
+ * {@link LeafEntry} per column. First-wins dedup by name (prod precedes staging).
6321
+ * Ids are {@link createGlobalPObjectId}-shaped — `resolvePath` is not involved.
6322
+ */
6323
+ function indexPoolBlock(block) {
6324
+ const accessors = [];
6325
+ if (block.prodCtx) accessors.push(block.prodCtx);
6326
+ if (block.stagingCtx) accessors.push(block.stagingCtx);
6327
+ const result = [];
6328
+ const seen = /* @__PURE__ */ new Set();
6329
+ for (const accessor of accessors) for (const name of listColumnNames(accessor)) {
6330
+ if (seen.has(name)) continue;
6331
+ seen.add(name);
6332
+ result.push({
6333
+ accessor,
6334
+ name,
6335
+ id: createGlobalPObjectId(block.blockId, name)
6336
+ });
6337
+ }
6338
+ return result;
6339
+ }
6340
+ //#endregion
6341
+ //#region ../../../../lib/model/common/dist/columns/column_registry.js
6342
+ /**
6343
+ * Id-index over a set of {@link ColumnEntriesProvider}s. Sole job:
6344
+ * {@link PObjectId} → {@link LeafEntry}. Generic over the accessor flavour, so
6345
+ * the same class backs both sandbox (`TreeNodeAccessor`) and host
6346
+ * (`PlTreeNodeAccessor`) usage.
6347
+ *
6348
+ * Stateless beyond the provider list — every lookup goes through each
6349
+ * provider's `getPObjectEntries()` (cached inside the provider). Instantiate
6350
+ * directly at the call site that has the providers; there is no ambient
6351
+ * singleton.
6352
+ */
6353
+ var ColumnRegistry = class {
6354
+ providers;
6355
+ constructor(providers) {
6356
+ this.providers = providers;
6357
+ }
6358
+ /**
6359
+ * Resolve a {@link PObjectId} to its backing {@link LeafEntry}. Returns
6360
+ * `undefined` if the column is not reachable from any provider — caller
6361
+ * decides whether that's `absent` or `resolving` via {@link isFinal}.
6362
+ */
6363
+ resolve(id) {
6364
+ return this.lookupById(id);
6365
+ }
6366
+ /** Whether every indexed source has finished enumerating its columns. */
6367
+ isFinal() {
6368
+ return this.providers.every((p) => p.isFinal());
6369
+ }
6370
+ lookupById(id) {
6371
+ for (const p of this.providers) {
6372
+ const hit = p.getPObjectEntries().get(id);
6373
+ if (hit !== void 0) return hit;
6374
+ }
6375
+ }
6376
+ };
6377
+ //#endregion
6378
+ //#region ../../../../lib/model/common/dist/columns/providers.js
6379
+ /**
6380
+ * Generic entries provider over a single accessor root. Walks `<root>` once
6381
+ * from the supplied `rootPath`, builds an id → {@link LeafEntry} map and
6382
+ * exposes `isFinal()` via the root's `getInputsLocked()`.
6383
+ *
6384
+ * Used directly on the host side; sandbox extends it with `getColumns()`
6385
+ * returning {@link ColumnLazy}s — see `AccessorColumnsProvider` in
6386
+ * `@platforma-sdk/model`.
6387
+ */
6388
+ var AccessorEntriesProvider = class {
6389
+ root;
6390
+ entries;
6391
+ constructor(root, rootPath) {
6392
+ this.root = root;
6393
+ const map = /* @__PURE__ */ new Map();
6394
+ for (const entry of indexAccessorRoot(root, rootPath)) if (!map.has(entry.id)) map.set(entry.id, entry);
6395
+ this.entries = map;
6396
+ }
6397
+ getPObjectEntries() {
6398
+ return this.entries;
6399
+ }
6400
+ isFinal() {
6401
+ return this.root.getInputsLocked();
6402
+ }
6403
+ };
6404
+ /**
6405
+ * Generic entries provider over a list of upstream-block ctx pairs.
6406
+ *
6407
+ * Per-block merge: iterate `prod` then `staging`, dedupe by name with
6408
+ * first-wins semantics (prod takes precedence).
6409
+ *
6410
+ * `isFinal()` is the AND of `getInputsLocked()` over every present ctx
6411
+ * accessor and `!prodIncomplete && !stagingIncomplete` over every block.
6412
+ */
6413
+ var ResultPoolEntriesProvider = class {
6414
+ blocks;
6415
+ cachedEntries;
6416
+ constructor(blocks) {
6417
+ this.blocks = blocks;
6418
+ }
6419
+ getPObjectEntries() {
6420
+ if (this.cachedEntries !== void 0) return this.cachedEntries;
6421
+ const map = /* @__PURE__ */ new Map();
6422
+ for (const block of this.blocks) for (const entry of indexPoolBlock(block)) if (!map.has(entry.id)) map.set(entry.id, entry);
6423
+ return this.cachedEntries = map;
6424
+ }
6425
+ isFinal() {
6426
+ for (const block of this.blocks) {
6427
+ if (block.prodIncomplete || block.stagingIncomplete) return false;
6428
+ if (block.prodCtx && !block.prodCtx.getInputsLocked()) return false;
6429
+ if (block.stagingCtx && !block.stagingCtx.getInputsLocked()) return false;
6430
+ }
6431
+ return true;
6432
+ }
6433
+ };
6434
+ //#endregion
6435
+ //#region ../../../../sdk/model/dist/render/accessor.js
6436
+ function ifDef(value, cb) {
6437
+ return value === void 0 ? void 0 : cb(value);
6438
+ }
6439
+ /**
6440
+ * Decode an error node's content into a display message. The backend serializes
6441
+ * a resource error as `{"message": "..."}` (`ResourceError`); unwrap that to the
6442
+ * human-readable message. Falls back to the raw string when the content is not
6443
+ * that envelope (e.g. plain text, or an unexpected shape).
6444
+ */
6445
+ function decodeErrorMessage(raw) {
6446
+ try {
6447
+ const parsed = JSON.parse(raw);
6448
+ if (typeof parsed?.message === "string") return parsed.message;
6449
+ } catch {}
6450
+ return raw;
6451
+ }
6452
+ /** Represent resource tree node accessor */
6453
+ var TreeNodeAccessor = class TreeNodeAccessor {
6454
+ handle;
5938
6455
  resolvePath;
5939
6456
  constructor(handle, resolvePath) {
5940
6457
  this.handle = handle;
@@ -5961,7 +6478,7 @@ var TreeNodeAccessor = class TreeNodeAccessor {
5961
6478
  }));
5962
6479
  return this.resolveWithCommon({}, ...transformedSteps);
5963
6480
  }
5964
- resolveAny(...steps) {
6481
+ traverse(...steps) {
5965
6482
  return this.resolveWithCommon({}, ...steps);
5966
6483
  }
5967
6484
  resolveWithCommon(commonOptions, ...steps) {
@@ -6007,6 +6524,9 @@ var TreeNodeAccessor = class TreeNodeAccessor {
6007
6524
  if (content == void 0) throw new Error("Resource has no content.");
6008
6525
  return JSON.parse(content);
6009
6526
  }
6527
+ hasData() {
6528
+ return getCfgRenderCtx().hasData(this.handle);
6529
+ }
6010
6530
  getDataBase64() {
6011
6531
  return getCfgRenderCtx().getDataBase64(this.handle);
6012
6532
  }
@@ -6043,9 +6563,16 @@ var TreeNodeAccessor = class TreeNodeAccessor {
6043
6563
  }
6044
6564
  return this.getDataAsJson();
6045
6565
  }
6046
- /**
6047
- *
6048
- */
6566
+ getFileContentAsBase64(range) {
6567
+ return new FutureRef(getCfgRenderCtx().getBlobContentAsBase64(this.handle, range));
6568
+ }
6569
+ getFileContentAsString(range) {
6570
+ return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range));
6571
+ }
6572
+ getFileContentAsJson(range) {
6573
+ return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range)).mapDefined((v) => JSON.parse(v));
6574
+ }
6575
+ /** @deprecated */
6049
6576
  getPColumns(errorOnUnknownField = false, prefix = "") {
6050
6577
  const result = this.parsePObjectCollection(errorOnUnknownField, prefix);
6051
6578
  if (result === void 0) return void 0;
@@ -6054,9 +6581,7 @@ var TreeNodeAccessor = class TreeNodeAccessor {
6054
6581
  return obj;
6055
6582
  });
6056
6583
  }
6057
- /**
6058
- *
6059
- */
6584
+ /** @deprecated */
6060
6585
  parsePObjectCollection(errorOnUnknownField = false, prefix = "") {
6061
6586
  const pObjects = getCfgRenderCtx().parsePObjectCollection(this.handle, errorOnUnknownField, prefix, ...this.resolvePath);
6062
6587
  if (pObjects === void 0) return void 0;
@@ -6067,15 +6592,6 @@ var TreeNodeAccessor = class TreeNodeAccessor {
6067
6592
  }
6068
6593
  return result;
6069
6594
  }
6070
- getFileContentAsBase64(range) {
6071
- return new FutureRef(getCfgRenderCtx().getBlobContentAsBase64(this.handle, range));
6072
- }
6073
- getFileContentAsString(range) {
6074
- return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range));
6075
- }
6076
- getFileContentAsJson(range) {
6077
- return new FutureRef(getCfgRenderCtx().getBlobContentAsString(this.handle, range)).mapDefined((v) => JSON.parse(v));
6078
- }
6079
6595
  /**
6080
6596
  * @deprecated use getFileContentAsBase64
6081
6597
  */
@@ -6167,8 +6683,8 @@ var TreeNodeAccessor = class TreeNodeAccessor {
6167
6683
  };
6168
6684
  //#endregion
6169
6685
  //#region ../../../../sdk/model/dist/render/internal.js
6170
- const StagingAccessorName = "staging";
6171
6686
  const MainAccessorName = "main";
6687
+ const StagingAccessorName = "staging";
6172
6688
  //#endregion
6173
6689
  //#region ../../../../sdk/model/dist/render/util/axis_filtering.js
6174
6690
  function filterDataInfoEntries(dataInfoEntries, axisFilters) {
@@ -6234,46 +6750,229 @@ function filterDataInfoEntries(dataInfoEntries, axisFilters) {
6234
6750
  }
6235
6751
  }
6236
6752
  //#endregion
6237
- //#region ../../../../node_modules/.pnpm/es-toolkit@1.39.10/node_modules/es-toolkit/dist/function/identity.mjs
6238
- function identity(x) {
6239
- return x;
6753
+ //#region ../../../../sdk/model/dist/labels/linked_column_postfix.js
6754
+ /**
6755
+ * Structural postfix derivation for linked columns (phase 2 of `deriveDistinctLabels`).
6756
+ *
6757
+ * A linked column is
6758
+ * reached by a CHAIN of linkers, and we distinguish two such columns by the same ideology the main
6759
+ * algorithm uses — "find the minimal difference, escalate until unique" — over two dimensions:
6760
+ *
6761
+ * - root: the single source axis of the chain, derived from `linkers[0]` (one of its axesSpec).
6762
+ * This is "the source" — it's what we prefer to show ("difference of sources").
6763
+ * - linkers: the linker chain itself (by `LinkLabel`), a per-step fallback used only where roots
6764
+ * coincide.
6765
+ *
6766
+ * Nothing is stored redundantly: the caller passes the linker path (`linkers`) and the hit spec;
6767
+ * root and every token are computed on the fly. The caller also supplies the `stem` (label+trace+
6768
+ * quals from the existing single-entity machinery); this module only adds the path postfix, and
6769
+ * only where stems collide.
6770
+ */
6771
+ function defaultLinkerFormatter(parts) {
6772
+ const chain = parts.linkers.map((l) => l.text).join(" > ");
6773
+ const pieces = [parts.root?.text, chain || void 0].filter((p) => !isNil(p));
6774
+ return pieces.length === 0 ? "" : `via ${pieces.join(" ")}`;
6240
6775
  }
6241
- //#endregion
6242
- //#region ../../../../node_modules/.pnpm/es-toolkit@1.39.10/node_modules/es-toolkit/dist/predicate/isFunction.mjs
6243
- function isFunction(value) {
6244
- return typeof value === "function";
6776
+ function extractAxisIdentity(axis) {
6777
+ const id = getAxisId(axis);
6778
+ return {
6779
+ label: readAnnotation(axis, Annotation.Label)?.trim() || void 0,
6780
+ name: id.name,
6781
+ domain: id.domain,
6782
+ contextDomain: id.contextDomain
6783
+ };
6245
6784
  }
6246
- //#endregion
6247
- //#region ../../../../node_modules/.pnpm/es-toolkit@1.39.10/node_modules/es-toolkit/dist/predicate/isNil.mjs
6248
- function isNil(x) {
6249
- return x == null;
6785
+ /**
6786
+ * A linker is named ONLY by its human label (`LinkLabel`/`Label`) — never by its technical column
6787
+ * name. Returns `undefined` for an unlabeled linker, which callers treat as "not renderable": such a
6788
+ * step is skipped, and disambiguation falls to another step or the root.
6789
+ */
6790
+ function extractLinkerIdentity(linker) {
6791
+ const label = (readAnnotation(linker, Annotation.LinkLabel) ?? readAnnotation(linker, Annotation.Label))?.trim();
6792
+ return label ? {
6793
+ label,
6794
+ name: label
6795
+ } : void 0;
6796
+ }
6797
+ /** Domain keys where `mine` differs from at least one competitor. */
6798
+ function differingKeys(mine, others) {
6799
+ const my = mine ?? {};
6800
+ const diffKeys = /* @__PURE__ */ new Set();
6801
+ for (const o of others) {
6802
+ const od = o ?? {};
6803
+ for (const k of Object.keys(my)) if (my[k] !== od[k]) diffKeys.add(k);
6804
+ for (const k of Object.keys(od)) if (my[k] !== od[k]) diffKeys.add(k);
6805
+ }
6806
+ return [...diffKeys];
6807
+ }
6808
+ function renderWithDomain(base, keys, domain) {
6809
+ if (keys.length === 0) return base;
6810
+ const d = domain ?? {};
6811
+ return `${base}[${keys.map((k) => `${k}=${d[k] ?? "∅"}`).join(", ")}]`;
6812
+ }
6813
+ /**
6814
+ * Minimal token distinguishing `mine` from `others`: label → name → only the differing domain keys.
6815
+ * Falls back to the bare label/name when indistinguishable at this dimension (another one covers it).
6816
+ */
6817
+ function deriveToken(mine, others) {
6818
+ const base = mine.label ?? mine.name;
6819
+ if (mine.label !== void 0 && others.every((o) => o.label !== mine.label)) return mine.label;
6820
+ if (others.every((o) => o.name !== mine.name)) return base;
6821
+ const dk = differingKeys(mine.domain, others.map((o) => o.domain));
6822
+ if (dk.length > 0) return renderWithDomain(base, dk, mine.domain);
6823
+ const ck = differingKeys(mine.contextDomain, others.map((o) => o.contextDomain));
6824
+ if (ck.length > 0) return renderWithDomain(base, ck, mine.contextDomain);
6825
+ return base;
6826
+ }
6827
+ /**
6828
+ * The chain's single root: the source-side axis of `linkers[0]`. A linker bridges two axis groups;
6829
+ * the side facing the next hop (`linkers[1]`, or the hit for a single-linker chain) is the target,
6830
+ * the other side is the source → its axis is the root. `undefined` if `linkers[0]` is not a
6831
+ * well-formed two-group linker.
6832
+ */
6833
+ function extractRoot(hit, linkers) {
6834
+ const first = linkers[0];
6835
+ if (first === void 0) return void 0;
6836
+ const axes = first.axesSpec;
6837
+ if (axes.length !== 2) return void 0;
6838
+ const second = linkers[1] ?? hit;
6839
+ return axes.find((a) => !second.axesSpec.some((b) => b.name === a.name));
6840
+ }
6841
+ const ABSENT = "__ABSENT__";
6842
+ function deriveSlotKey(group, slot, row) {
6843
+ if (slot.kind === "root") {
6844
+ const r = group.roots[row];
6845
+ return r === void 0 ? ABSENT : canonicalizeJson(getAxisId(r));
6846
+ } else {
6847
+ const l = group.entries[row].linkers?.[slot.i];
6848
+ const id = l && extractLinkerIdentity(l);
6849
+ return isNil(id) ? ABSENT : canonicalizeJson(id);
6850
+ }
6851
+ }
6852
+ function deriveSlotToken(group, slot, row) {
6853
+ if (slot.kind === "root") {
6854
+ const r = group.roots[row];
6855
+ if (r === void 0) return void 0;
6856
+ const comp = group.roots.filter((o, j) => j !== row && !isNil(o)).map((o) => extractAxisIdentity(o));
6857
+ return deriveToken(extractAxisIdentity(r), comp);
6858
+ }
6859
+ const l = group.entries[row].linkers?.[slot.i];
6860
+ const id = l && extractLinkerIdentity(l);
6861
+ if (isNil(id)) return void 0;
6862
+ return deriveToken(id, group.entries.filter((_, j) => j !== row).map((e) => e.linkers?.[slot.i]).map((x) => x ? extractLinkerIdentity(x) : void 0).filter((x) => !isNil(x)));
6863
+ }
6864
+ function getDiscriminates(group, slot) {
6865
+ return new Set(group.entries.map((_, r) => deriveSlotKey(group, slot, r))).size > 1;
6866
+ }
6867
+ /** Render one row against a chosen slot set: the distinguishing root + linker pieces, formatted. */
6868
+ function renderRow(group, slots, row) {
6869
+ const rootSpec = group.roots[row];
6870
+ const rootText = slots.some((s) => s.kind === "root") ? deriveSlotToken(group, { kind: "root" }, row) : void 0;
6871
+ const root = rootSpec !== void 0 && rootText !== void 0 ? {
6872
+ spec: rootSpec,
6873
+ text: rootText
6874
+ } : void 0;
6875
+ const linkers = slots.filter((s) => s.kind === "linker").sort((a, b) => a.i - b.i).map((s) => {
6876
+ const spec = group.entries[row].linkers?.[s.i];
6877
+ const text = deriveSlotToken(group, s, row);
6878
+ return spec !== void 0 && text !== void 0 ? {
6879
+ spec,
6880
+ text
6881
+ } : void 0;
6882
+ }).filter((l) => !isNil(l));
6883
+ if (root === void 0 && linkers.length === 0) return "";
6884
+ return group.format({
6885
+ root,
6886
+ linkers
6887
+ }, group.entries[row].hit, group.indices[row]) || "";
6888
+ }
6889
+ function renderAll(group, slots) {
6890
+ return group.entries.map((_, r) => renderRow(group, slots, r));
6891
+ }
6892
+ function allUnique(rendered) {
6893
+ return new Set(rendered).size === rendered.length;
6894
+ }
6895
+ /**
6896
+ * Minimal slot set that makes the group unique. Escalate by priority (root, then linkers by step),
6897
+ * then drop any redundant slot; render every row symmetrically against the result.
6898
+ *
6899
+ * KNOWN LIMITATION (review point): symmetric render can over-decorate a row in a mixed group (e.g.
6900
+ * `via Sample MapperA` where `via Sample` alone is already unique for that row). Per-row trimming is
6901
+ * a generalized `dropRedundantLinkerSuffix`; naive greedy trimming is unstable, so it's deferred.
6902
+ */
6903
+ function resolveGroup(entries, indices, format) {
6904
+ const group = {
6905
+ entries,
6906
+ roots: entries.map((e) => e.hit && e.linkers?.length ? extractRoot(e.hit, e.linkers) : void 0),
6907
+ indices,
6908
+ format
6909
+ };
6910
+ const maxLen = Math.max(0, ...entries.map((e) => e.linkers?.length ?? 0));
6911
+ const escalated = [{ kind: "root" }, ...Array.from({ length: maxLen }, (_, i) => ({
6912
+ kind: "linker",
6913
+ i
6914
+ }))].reduce((acc, slot) => allUnique(renderAll(group, acc)) || !getDiscriminates(group, slot) ? acc : (acc.push(slot), acc), []);
6915
+ return renderAll(group, escalated.reduce((acc, slot) => {
6916
+ const trial = acc.filter((s) => s !== slot);
6917
+ return allUnique(renderAll(group, trial)) ? trial : acc;
6918
+ }, escalated));
6919
+ }
6920
+ /**
6921
+ * Full label per entry: `stem` plus, where stems collide, a minimal postfix distinguishing the
6922
+ * linked columns by the difference between their sources.
6923
+ */
6924
+ function derivePostfixes(entries, format = defaultLinkerFormatter) {
6925
+ const postfix = [...entries.reduce((acc, e, idx) => acc.set(e.stem, [...acc.get(e.stem) ?? [], idx]), /* @__PURE__ */ new Map()).values()].reduce((acc, idxs) => {
6926
+ if (idxs.length < 2) return acc;
6927
+ const resolved = resolveGroup(idxs.map((i) => entries[i]), idxs, format);
6928
+ return idxs.reduce((m, i, k) => m.set(i, resolved[k]), acc);
6929
+ }, /* @__PURE__ */ new Map());
6930
+ return entries.map((e, i) => {
6931
+ const p = postfix.get(i);
6932
+ return p ? `${e.stem} ${p}` : e.stem;
6933
+ });
6250
6934
  }
6251
6935
  //#endregion
6252
6936
  //#region ../../../../sdk/model/dist/labels/derive_distinct_labels.js
6253
6937
  const DISTANCE_PENALTY = .001;
6254
6938
  const LABEL_TYPE = "__LABEL__";
6255
6939
  const LABEL_TYPE_FULL = "__LABEL__@1";
6256
- const LINKER_TYPE = "__LINKER__";
6257
- const LINKER_TYPE_FULL = "__LINKER__@1";
6258
6940
  const HIT_QUAL_TYPE = "__HIT_QUAL__";
6259
6941
  const ANCHOR_QUAL_TYPE_PREFIX = "__ANCHOR_QUAL__:";
6260
6942
  function isAnchorQualType(t) {
6261
6943
  return t.startsWith(ANCHOR_QUAL_TYPE_PREFIX);
6262
6944
  }
6263
6945
  function isSyntheticType(t) {
6264
- return t === LINKER_TYPE || t === HIT_QUAL_TYPE || isAnchorQualType(t);
6946
+ return t === HIT_QUAL_TYPE || isAnchorQualType(t);
6265
6947
  }
6948
+ /**
6949
+ * Distinct labels for a set of columns. Two phases:
6950
+ * 1. {@link deriveStems} — treats each column as a single entity (native label + trace + hit/anchor
6951
+ * qualifications) and produces the minimal distinguishing "stem".
6952
+ * 2. {@link derivePostfixes} — for columns still colliding on their stem, appends a "via …" postfix
6953
+ * describing the difference between their linker sources (root axis, then linker chain).
6954
+ */
6266
6955
  function deriveDistinctLabels(values, options = {}) {
6956
+ const stems = deriveStems(values, options);
6957
+ return derivePostfixes(values.map((v, i) => {
6958
+ const { spec, linkerPath } = extractEntryParts(v);
6959
+ return {
6960
+ stem: stems[i],
6961
+ hit: spec,
6962
+ linkers: (linkerPath ?? []).map((s) => s.spec)
6963
+ };
6964
+ }), options.formatters?.linker);
6965
+ }
6966
+ /** Phase 1: minimal per-column stem — native label + trace + hit/anchor qualifications, no linkers. */
6967
+ function deriveStems(values, options) {
6267
6968
  const forceTraceElements = options.forceTraceElements !== void 0 && options.forceTraceElements.length > 0 ? new Set(options.forceTraceElements) : void 0;
6268
6969
  const separator = options.separator ?? " / ";
6269
6970
  const records = values.map((v, i) => enrichRecord(v, i, options));
6270
6971
  const stats = collectTypeStats(records);
6271
6972
  const hasAnySynthetic = records.some((r) => r.fullTrace.some((ft) => isSyntheticType(ft.type)));
6272
6973
  const labelForced = (options.includeNativeLabel === true || hasAnySynthetic) && stats.countByType.has(LABEL_TYPE_FULL);
6273
- const linkerForced = stats.countByType.get(LINKER_TYPE_FULL) === values.length;
6274
6974
  const forcedSet = /* @__PURE__ */ new Set();
6275
6975
  if (labelForced) forcedSet.add(LABEL_TYPE_FULL);
6276
- if (linkerForced) forcedSet.add(LINKER_TYPE_FULL);
6277
6976
  const { mainTypes, secondaryTypes } = classifyTypes(stats, values.length);
6278
6977
  const build = (typeSet, force) => buildLabels(records, typeSet, forceTraceElements, separator, force);
6279
6978
  if (mainTypes.length === 0) {
@@ -6289,7 +6988,7 @@ function deriveDistinctLabels(values, options = {}) {
6289
6988
  const candidateResult = build(currentSet, false);
6290
6989
  if (candidateResult !== void 0 && countUniqueLabels(candidateResult) === values.length) {
6291
6990
  const minimized = minimizeTypeSet(currentSet, records, stats, forceTraceElements, forcedSet, separator);
6292
- return dropRedundantLinkerSuffix(records, minimized, forceTraceElements, forcedSet, separator, build(minimized, false) ?? throwError("Failed to derive unique labels"));
6991
+ return repairBareByPresence(build(minimized, false) ?? throwError("Failed to derive unique labels"), minimized, records, stats, forcedSet, forceTraceElements, separator);
6293
6992
  }
6294
6993
  additionalType++;
6295
6994
  if (additionalType >= mainTypes.length) {
@@ -6302,7 +7001,7 @@ function deriveDistinctLabels(values, options = {}) {
6302
7001
  ...mainTypes,
6303
7002
  ...secondaryTypes
6304
7003
  ]), records, stats, forceTraceElements, forcedSet, separator);
6305
- return dropRedundantLinkerSuffix(records, minimized, forceTraceElements, forcedSet, separator, build(minimized, true) ?? throwError("Failed to derive unique labels"));
7004
+ return repairBareByPresence(build(minimized, true) ?? throwError("Failed to derive unique labels"), minimized, records, stats, forcedSet, forceTraceElements, separator);
6306
7005
  }
6307
7006
  function extractEntryParts(entry) {
6308
7007
  if (!("spec" in entry && typeof entry.spec === "object")) return {
@@ -6328,13 +7027,6 @@ function formatQualification(q) {
6328
7027
  function formatQualifications(qs) {
6329
7028
  return qs.map(formatQualification).join("; ");
6330
7029
  }
6331
- function computeStepLabel(step, stepIndex, formatters) {
6332
- const base = (readAnnotation(step.spec, Annotation.LinkLabel) ?? readAnnotation(step.spec, Annotation.Label))?.trim();
6333
- if (isNil(base) || base.length === 0) return void 0;
6334
- if (step.qualifications === void 0 || step.qualifications.length === 0) return base;
6335
- const qualText = isFunction(formatters?.linkerStepQualification) ? formatters.linkerStepQualification(step.qualifications, stepIndex, step.spec) : `[${formatQualifications(step.qualifications)}]`;
6336
- return isNil(qualText) ? base : `${base} ${qualText}`;
6337
- }
6338
7030
  function buildFullTrace(trace) {
6339
7031
  const result = [];
6340
7032
  const occurrences = /* @__PURE__ */ new Map();
@@ -6352,7 +7044,7 @@ function buildFullTrace(trace) {
6352
7044
  return result;
6353
7045
  }
6354
7046
  function enrichRecord(value, index, options) {
6355
- const { spec, extraTrace, linkerPath, qualifications } = extractEntryParts(value);
7047
+ const { spec, extraTrace, qualifications } = extractEntryParts(value);
6356
7048
  const formatters = options.formatters;
6357
7049
  const rawLabel = readAnnotation(spec, Annotation.Label);
6358
7050
  const traceStr = readAnnotation(spec, Annotation.Trace);
@@ -6376,17 +7068,6 @@ function enrichRecord(value, index, options) {
6376
7068
  else trace.splice(0, 0, labelEntry);
6377
7069
  }
6378
7070
  }
6379
- if (linkerPath !== void 0 && linkerPath.length > 0) {
6380
- const stepLabels = linkerPath.map((step, i) => computeStepLabel(step, i, formatters)).filter((s) => !isNil(s));
6381
- if (stepLabels.length > 0) {
6382
- const linkerText = isFunction(formatters?.linker) ? formatters.linker(stepLabels, spec, index) : `via ${stepLabels.join(" > ")}`;
6383
- if (!isNil(linkerText)) trace.push({
6384
- type: LINKER_TYPE,
6385
- label: linkerText,
6386
- importance: -10
6387
- });
6388
- }
6389
- }
6390
7071
  if (qualifications !== void 0 && qualifications.forQueries !== void 0) {
6391
7072
  for (const [anchorId, qs] of Object.entries(qualifications.forQueries)) {
6392
7073
  if (qs.length === 0) continue;
@@ -6438,21 +7119,18 @@ function classifyTypes(stats, totalRecords) {
6438
7119
  function renderRecordLabel(record, includedTypes, forceTraceElements, separator) {
6439
7120
  const traceParts = [];
6440
7121
  const anchorParts = [];
6441
- let linkerLabel;
6442
7122
  let hitLabel;
6443
7123
  for (const ft of record.fullTrace) {
6444
7124
  if (!(includedTypes.has(ft.fullType) || forceTraceElements?.has(ft.type))) continue;
6445
- if (ft.type === LINKER_TYPE) linkerLabel = ft.label;
6446
- else if (ft.type === HIT_QUAL_TYPE) hitLabel = ft.label;
7125
+ if (ft.type === HIT_QUAL_TYPE) hitLabel = ft.label;
6447
7126
  else if (isAnchorQualType(ft.type)) anchorParts.push(ft.label);
6448
7127
  else traceParts.push(ft.label);
6449
7128
  }
6450
- if (traceParts.length === 0 && anchorParts.length === 0 && linkerLabel === void 0 && hitLabel === void 0) return void 0;
7129
+ if (traceParts.length === 0 && anchorParts.length === 0 && hitLabel === void 0) return void 0;
6451
7130
  let label = traceParts.join(separator);
6452
7131
  const append = (part) => {
6453
7132
  label = label.length === 0 ? part : `${label} ${part}`;
6454
7133
  };
6455
- if (linkerLabel !== void 0) append(linkerLabel);
6456
7134
  for (const a of anchorParts) append(a);
6457
7135
  if (hitLabel !== void 0) append(hitLabel);
6458
7136
  return label;
@@ -6470,32 +7148,6 @@ function buildLabels(records, includedTypes, forceTraceElements, separator, forc
6470
7148
  }
6471
7149
  return result;
6472
7150
  }
6473
- /**
6474
- * Drop the "via …" linker suffix from records whose label is already unique without it.
6475
- *
6476
- * Global minimization may include `LINKER_TYPE_FULL` solely to resolve a collision between a
6477
- * subset of records — but `buildLabels` then renders the suffix on every record that carries a
6478
- * linker trace entry, including ones whose stem is already unique. We strip the suffix where it
6479
- * isn't load-bearing while keeping the symmetric rendering required by `linkerForced` /
6480
- * `forceTraceElements`.
6481
- *
6482
- * Rule: a record's linker suffix is redundant iff its stem (label rendered without LINKER) does
6483
- * not appear anywhere else in the set.
6484
- */
6485
- function dropRedundantLinkerSuffix(records, globalTypeSet, forceTraceElements, forcedSet, separator, labels) {
6486
- if (!globalTypeSet.has(LINKER_TYPE_FULL)) return labels;
6487
- if (forcedSet.has(LINKER_TYPE_FULL) || forceTraceElements?.has(LINKER_TYPE)) return labels;
6488
- const setWithoutLinker = new Set(globalTypeSet);
6489
- setWithoutLinker.delete(LINKER_TYPE_FULL);
6490
- const stems = records.map((r) => renderRecordLabel(r, setWithoutLinker, forceTraceElements, separator));
6491
- const stemOccurrences = /* @__PURE__ */ new Map();
6492
- for (const s of stems) if (s !== void 0) stemOccurrences.set(s, (stemOccurrences.get(s) ?? 0) + 1);
6493
- return labels.map((label, i) => {
6494
- const stem = stems[i];
6495
- if (stem === void 0) return label;
6496
- return stemOccurrences.get(stem) === 1 ? stem : label;
6497
- });
6498
- }
6499
7151
  function countUniqueLabels(result) {
6500
7152
  if (result === void 0) return 0;
6501
7153
  return new Set(result).size;
@@ -6514,6 +7166,60 @@ function minimizeTypeSet(typeSet, records, stats, forceTraceElements, forcedSet,
6514
7166
  }
6515
7167
  return result;
6516
7168
  }
7169
+ const ABSENT_VALUE = "\0absent";
7170
+ /** Whether `fullType`'s rendered value differs across the group (absence counts as a value). */
7171
+ function typeDistinguishes(records, group, fullType) {
7172
+ const values = /* @__PURE__ */ new Set();
7173
+ for (const i of group) {
7174
+ const ft = records[i].fullTrace.find((e) => e.fullType === fullType);
7175
+ values.add(ft?.label ?? ABSENT_VALUE);
7176
+ if (values.size > 1) return true;
7177
+ }
7178
+ return false;
7179
+ }
7180
+ /** The highest-importance trace type this row HAS (outside `typeSet`) that sets it apart from its
7181
+ * group peers. `undefined` when the row carries nothing distinguishing of its own. */
7182
+ function bestDistinguishingType(records, group, row, typeSet, forcedSet, forceTraceElements, stats) {
7183
+ return records[row].fullTrace.reduce((best, ft) => {
7184
+ if (typeSet.has(ft.fullType) || forcedSet.has(ft.fullType) || forceTraceElements?.has(ft.type)) return best;
7185
+ if (!typeDistinguishes(records, group, ft.fullType)) return best;
7186
+ const imp = stats.importances.get(ft.fullType) ?? 0;
7187
+ return best === void 0 || imp > best.imp ? {
7188
+ type: ft.fullType,
7189
+ imp
7190
+ } : best;
7191
+ }, void 0)?.type;
7192
+ }
7193
+ /**
7194
+ * Un-bare columns distinguished only "by absence". After minimization a column can end up with a
7195
+ * bare trace zone (only the forced native label) while a peer sharing that same base renders extra
7196
+ * tokens — the column reads as unique purely because it LACKS what the peer has. For each such bare
7197
+ * column this re-renders JUST that column's label with the highest-importance trace type it actually
7198
+ * carries that tells it apart from its peers, so every colliding column is distinguished by a token
7199
+ * it HAS rather than by omission.
7200
+ *
7201
+ * Patches individual labels rather than the shared type set on purpose: the set is global, so adding
7202
+ * a type there would also decorate unrelated columns in other groups that happen to carry it. Only
7203
+ * bare columns' labels grow (a superset of their previous value), so uniqueness is preserved. Groups
7204
+ * are keyed by the base = the label rendered from the forced types alone.
7205
+ */
7206
+ function repairBareByPresence(labels, minimized, records, stats, forcedSet, forceTraceElements, separator) {
7207
+ const base = records.map((r) => renderRecordLabel(r, forcedSet, forceTraceElements, separator));
7208
+ const isBare = records.map((r, i) => renderRecordLabel(r, minimized, forceTraceElements, separator) === base[i]);
7209
+ const groups = records.reduce((acc, _, i) => acc.set(base[i] ?? "", [...acc.get(base[i] ?? "") ?? [], i]), /* @__PURE__ */ new Map());
7210
+ const patched = [...labels];
7211
+ for (const group of groups.values()) {
7212
+ if (!group.some((i) => !isBare[i])) continue;
7213
+ for (const i of group) {
7214
+ if (!isBare[i]) continue;
7215
+ const chosen = bestDistinguishingType(records, group, i, minimized, forcedSet, forceTraceElements, stats);
7216
+ if (chosen === void 0) continue;
7217
+ const withToken = /* @__PURE__ */ new Set([...minimized, chosen]);
7218
+ patched[i] = renderRecordLabel(records[i], withToken, forceTraceElements, separator) ?? patched[i];
7219
+ }
7220
+ }
7221
+ return patched;
7222
+ }
6517
7223
  //#endregion
6518
7224
  //#region ../../../../sdk/model/dist/render/util/label.js
6519
7225
  /** @deprecated Use deriveDistinctLabels */
@@ -6811,7 +7517,7 @@ function isPColumnReady(c) {
6811
7517
  const isValues = (d) => Array.isArray(d);
6812
7518
  const isAccessor = (d) => d instanceof TreeNodeAccessor;
6813
7519
  let ready = true;
6814
- const data = typeof c.data === "function" ? c.data() : c.data;
7520
+ const data = c.data;
6815
7521
  if (data == null) return false;
6816
7522
  else if (isAccessor(data)) ready &&= data.getIsReadyOrError();
6817
7523
  else if (isDataInfo(data)) visitDataInfo(data, (v) => ready &&= v.getIsReadyOrError());
@@ -6832,7 +7538,7 @@ function isPColumnValues(value) {
6832
7538
  /**
6833
7539
  * A simple implementation of {@link ColumnProvider} backed by a pre-defined array of columns.
6834
7540
  */
6835
- var ArrayColumnProvider = class {
7541
+ var ArrayColumnsProvider = class {
6836
7542
  columns;
6837
7543
  constructor(columns) {
6838
7544
  this.columns = columns;
@@ -6884,7 +7590,7 @@ function getSplitAxisIndices(selector) {
6884
7590
  }
6885
7591
  var PColumnCollection = class {
6886
7592
  defaultProviderStore = [];
6887
- providers = [new ArrayColumnProvider(this.defaultProviderStore)];
7593
+ providers = [new ArrayColumnsProvider(this.defaultProviderStore)];
6888
7594
  axisLabelProviders = [];
6889
7595
  constructor() {}
6890
7596
  addColumnProvider(provider) {
@@ -7047,7 +7753,9 @@ var PColumnCollection = class {
7047
7753
  result.push({
7048
7754
  id: finalId,
7049
7755
  spec: finalSpec,
7050
- data: () => entry.type === "split" ? entriesToDataInfo(filterDataInfoEntries(entry.dataEntries, axisFiltersTuple)) : entry.originalColumn.data,
7756
+ get data() {
7757
+ return entry.type === "split" ? entriesToDataInfo(filterDataInfoEntries(entry.dataEntries, axisFiltersTuple)) : entry.originalColumn.data;
7758
+ },
7051
7759
  label
7052
7760
  });
7053
7761
  }
@@ -7083,7 +7791,7 @@ var PColumnCollection = class {
7083
7791
  if (!entries) return void 0;
7084
7792
  const columns = [];
7085
7793
  for (const entry of entries) {
7086
- const data = entry.data();
7794
+ const data = entry.data;
7087
7795
  if (!data) {
7088
7796
  if (opts?.dontWaitAllData) continue;
7089
7797
  return;
@@ -7109,7 +7817,8 @@ var PColumnCollection = class {
7109
7817
  const BLOCK_SERVICE_FLAGS = {
7110
7818
  requiresPFrameSpec: true,
7111
7819
  requiresPFrame: true,
7112
- requiresDialog: true
7820
+ requiresDialog: true,
7821
+ requiresColumnsCollection: true
7113
7822
  };
7114
7823
  resolveRequiredServices(BLOCK_SERVICE_FLAGS);
7115
7824
  //#endregion
@@ -7124,8 +7833,8 @@ function createServiceProxy(dispatch) {
7124
7833
  //#endregion
7125
7834
  //#region ../../../../sdk/model/dist/services/get_services.js
7126
7835
  const cachedServices = /* @__PURE__ */ new WeakMap();
7127
- function getService(name) {
7128
- const ctx = getCfgRenderCtx();
7836
+ function getService(name, deps) {
7837
+ const ctx = deps?.ctx ?? getCfgRenderCtx();
7129
7838
  const map = cachedServices.has(ctx) ? cachedServices.get(ctx) : (() => {
7130
7839
  cachedServices.set(ctx, /* @__PURE__ */ new Map());
7131
7840
  return cachedServices.get(ctx);
@@ -7189,24 +7898,35 @@ function matchDomain(query, target) {
7189
7898
  }
7190
7899
  /**
7191
7900
  * Transforms PColumn data into the internal representation expected by the platform
7192
- * @param data Data from a PColumn to transform
7901
+ * @param column Data from a PColumn to transform
7193
7902
  * @returns Transformed data compatible with platform API
7194
7903
  */
7195
- function transformPColumnData(data) {
7196
- return mapPObjectData(data, (d) => {
7197
- if (d instanceof TreeNodeAccessor) return d.handle;
7198
- else if (isDataInfo(d)) return mapDataInfo(d, (accessor) => accessor.handle);
7199
- else return d ?? [];
7904
+ function finalizePColumnData(column) {
7905
+ return mapPObjectData(column, (data) => {
7906
+ if (data instanceof TreeNodeAccessor) return data.handle;
7907
+ else if (isDataInfo(data)) {
7908
+ let ready = true;
7909
+ visitDataInfo(data, (accessor) => ready &&= accessor?.getIsReadyOrError() ?? false);
7910
+ if (!ready) return [];
7911
+ return mapDataInfo(data, (accessor) => accessor?.handle ?? throwError("Accessor handle is undefined"));
7912
+ } else return data ?? [];
7200
7913
  });
7201
7914
  }
7915
+ /**
7916
+ * @deprecated use the direct `ctx.*` methods on `RenderCtxBase` instead.
7917
+ *
7918
+ * Migration map:
7919
+ * - `getSpecs` → `ctx.getSpecs`
7920
+ * - `getSpecByRef` / `getPColumnSpecByRef` → `ctx.getSpecByRef` / `ctx.getPColumnSpecByRef`
7921
+ * - `getDataByRef` / `getPColumnByRef` → `ctx.getDataByRef` / `ctx.getPColumnByRef`
7922
+ * - `getOptions` → `ctx.getOptions`
7923
+ *
7924
+ * Eager methods `selectColumns` / `getData` / `getDataWithErrors` should be
7925
+ * replaced by `ctx.getSpecs` + on-demand `ctx.getStatusByRef` /
7926
+ * `ctx.getPColumnByRef` so that the data resource is not subscribed up-front.
7927
+ */
7202
7928
  var ResultPool = class {
7203
7929
  ctx = getCfgRenderCtx();
7204
- /**
7205
- * @deprecated use getOptions()
7206
- */
7207
- calculateOptions(predicate) {
7208
- return this.ctx.calculateOptions(predicate);
7209
- }
7210
7930
  getOptions(predicateOrSelector, opts) {
7211
7931
  const predicate = typeof predicateOrSelector === "function" ? predicateOrSelector : legacyColumnSelectorsToPredicate(predicateOrSelector);
7212
7932
  const filtered = this.getSpecs().entries.filter((s) => predicate(s.obj));
@@ -7350,7 +8070,6 @@ var ResultPool = class {
7350
8070
  * @returns data associated with the ref
7351
8071
  */
7352
8072
  getDataByRef(ref) {
7353
- if (typeof this.ctx.getDataFromResultPoolByRef === "undefined") return this.getData().entries.find((f) => f.ref.blockId === ref.blockId && f.ref.name === ref.name)?.obj;
7354
8073
  const data = this.ctx.getDataFromResultPoolByRef(ref.blockId, ref.name);
7355
8074
  if (!data) return void 0;
7356
8075
  return mapPObjectData(data, (handle) => new TreeNodeAccessor(handle, [ref.blockId, ref.name]));
@@ -7431,6 +8150,9 @@ var ResultPool = class {
7431
8150
  *
7432
8151
  * @param selectors - A predicate function, a single selector, or an array of selectors.
7433
8152
  * @returns An array of PColumn objects matching the selectors. Data is loaded on first access.
8153
+ * @deprecated use `RenderCtxBase.rawResultPool` + `ResultPoolColumnsProvider` +
8154
+ * `discoverColumns` instead. This eager path materializes column data
8155
+ * unnecessarily for callers that only filter by spec.
7434
8156
  */
7435
8157
  selectColumns(selectors) {
7436
8158
  const predicate = typeof selectors === "function" ? selectors : legacyColumnSelectorsToPredicate(selectors);
@@ -7508,20 +8230,8 @@ var RenderCtxBase = class {
7508
8230
  get outputs() {
7509
8231
  return this.getNamedAccessor(MainAccessorName);
7510
8232
  }
8233
+ /** @deprecated use `rawResultPool` + `ResultPoolColumnsProvider` instead. */
7511
8234
  resultPool = new ResultPool();
7512
- /**
7513
- * Find labels data for a given axis id. It will search for a label column and return its data as a map.
7514
- * @returns a map of axis value => label
7515
- * @deprecated Use resultPool.findLabels instead
7516
- */
7517
- findLabels(axis) {
7518
- return this.resultPool.findLabels(axis);
7519
- }
7520
- verifyInlineAndExplicitColumnsSupport(columns) {
7521
- const hasInlineColumns = columns.some((c) => !(c.data instanceof TreeNodeAccessor) || isDataInfo(c.data));
7522
- const inlineColumnsSupport = this.ctx.featureFlags?.inlineColumnsSupport === true;
7523
- if (hasInlineColumns && !inlineColumnsSupport) throw Error(`Inline or explicit columns not supported`);
7524
- }
7525
8235
  patchPTableDef(def) {
7526
8236
  if (!this.ctx.featureFlags?.pTablePartitionFiltersSupport) def = {
7527
8237
  ...def,
@@ -7536,7 +8246,7 @@ var RenderCtxBase = class {
7536
8246
  return def;
7537
8247
  }
7538
8248
  createPFrame(def) {
7539
- return this.ctx.createPFrame(def.map((c) => transformPColumnData(c)));
8249
+ return this.ctx.createPFrame(def.map((v) => isString(v) ? v : finalizePColumnData(v)));
7540
8250
  }
7541
8251
  createPTable(def) {
7542
8252
  let rawDef;
@@ -7553,17 +8263,11 @@ var RenderCtxBase = class {
7553
8263
  sorting: def.sorting ?? []
7554
8264
  });
7555
8265
  else rawDef = this.patchPTableDef(def);
7556
- const columns = extractAllColumns(rawDef.src);
7557
- this.verifyInlineAndExplicitColumnsSupport(columns);
7558
- if (!allPColumnsReady(columns)) return void 0;
7559
- return this.ctx.createPTable(mapPTableDef(rawDef, (po) => transformPColumnData(po)));
8266
+ if (!allPColumnsReady(extractAllColumns(rawDef.src))) return void 0;
8267
+ return this.ctx.createPTable(mapPTableDef(rawDef, finalizePColumnData));
7560
8268
  }
7561
8269
  createPTableV2(def) {
7562
- return this.ctx.createPTableV2(mapPTableDefV2(def, (po) => transformPColumnData(po)));
7563
- }
7564
- /** @deprecated scheduled for removal from SDK */
7565
- getBlockLabel(blockId) {
7566
- return this.ctx.getBlockLabel(blockId);
8270
+ return this.ctx.createPTableV2(mapPTableDefV2(def, { column: (v) => isString(v) ? v : finalizePColumnData(v) }));
7567
8271
  }
7568
8272
  getCurrentUnstableMarker() {
7569
8273
  return this.ctx.getCurrentUnstableMarker();
@@ -7629,7 +8333,7 @@ var PluginRenderCtx = class extends RenderCtxBase {
7629
8333
  };
7630
8334
  //#endregion
7631
8335
  //#region ../../../../sdk/model/dist/version.js
7632
- const PlatformaSDKVersion = "1.79.20";
8336
+ const PlatformaSDKVersion = "1.80.8";
7633
8337
  //#endregion
7634
8338
  //#region ../../../../sdk/model/dist/bconfig/types.js
7635
8339
  function isConfigLambda(cfgOrFh) {
@@ -8137,79 +8841,2124 @@ var BlockModelV3 = class BlockModelV3 {
8137
8841
  }
8138
8842
  };
8139
8843
  //#endregion
8140
- //#region ../../../../sdk/model/dist/filters/traverse.js
8844
+ //#region ../../../../sdk/model/dist/columns/column_recipes/leaf_rebrand.js
8141
8845
  /**
8142
- * Recursively traverses a FilterSpec tree bottom-up, applying visitor callbacks.
8143
- *
8144
- * Entries with `{ type: undefined }` inside `and`/`or` arrays are skipped
8145
- * (these represent unfilled filter slots in the UI).
8846
+ * Replace every column leaf whose id equals `fromId` with `toId`, leaving
8847
+ * other column refs (linkers, sub-anchors) intact. Wrapper recipes
8848
+ * (Overridden / Filtered) and Discovered use this to lift their own id onto
8849
+ * the hit leaf, so per-variant uniqueness propagates to the engine and
8850
+ * resolver-emitted ids match leaves in the emitted SpecQuery.
8146
8851
  *
8147
- * Traversal order:
8148
- * 1. Recurse into child filters (`and`/`or`/`not`)
8149
- * 2. Apply the corresponding visitor callback with already-traversed children
8150
- * 3. For leaf nodes, call `leaf` directly
8852
+ * Generic over all SpecQuery node shapes — including `linkerJoin`, which
8853
+ * occurs when a wrapper sits over a {@link ColumnDiscoveredRecipe}: only
8854
+ * the deepest hit leaf carries `fromId`, every linker leaf has a different
8855
+ * id and is left untouched.
8151
8856
  */
8152
- function traverseFilterSpec(filter, visitor) {
8153
- return traverseFilterSpecImpl(filter, visitor);
8857
+ function rebrandLeafId(node, fromId, toId) {
8858
+ if (fromId === toId) return node;
8859
+ return mapSpecQueryColumns(node, { column: (id) => id === fromId ? toId : id });
8154
8860
  }
8155
- /** Internal implementation with simple generics for clean recursion. */
8156
- function traverseFilterSpecImpl(filter, visitor) {
8157
- switch (filter.type) {
8158
- case "and": return visitor.and(filter.filters.filter((f) => f.type !== void 0).map((f) => traverseFilterSpecImpl(f, visitor)));
8159
- case "or": return visitor.or(filter.filters.filter((f) => f.type !== void 0).map((f) => traverseFilterSpecImpl(f, visitor)));
8160
- case "not": return visitor.not(traverseFilterSpecImpl(filter.filter, visitor));
8161
- default: return visitor.leaf(filter);
8861
+ //#endregion
8862
+ //#region ../../../../sdk/model/dist/columns/column_recipes/column_overrided_recipe.js
8863
+ /**
8864
+ * Recipe wrapper that overlays {@link SpecOverrides} on top of any other
8865
+ * recipe. The id is `ColumnOverriddenKey { source: inner.id, specOverrides }`.
8866
+ *
8867
+ * Flat-merge invariant: `inner` is never another {@link ColumnOverriddenRecipe}.
8868
+ * Repeated overrides go through {@link wrap} / {@link withSpecs}, which merge
8869
+ * `specOverrides` at the current level and keep wrapper depth constant.
8870
+ *
8871
+ * This invariant matches the one enforced inside `unwrapOverrides` from
8872
+ * pl-model-common: it explicitly throws on a nested override-wrap.
8873
+ */
8874
+ var ColumnOverriddenRecipe = class ColumnOverriddenRecipe {
8875
+ inner;
8876
+ overrides;
8877
+ /**
8878
+ * Wrap any recipe with overrides. If `inner` is already a
8879
+ * {@link ColumnOverriddenRecipe}, merges `overrides` into the existing ones
8880
+ * and returns a new Overridden sharing the original `inner`. No
8881
+ * `Overridden<Overridden<X>>` is produced.
8882
+ */
8883
+ static wrap(col, overrides) {
8884
+ if (col instanceof ColumnOverriddenRecipe) return new ColumnOverriddenRecipe(col.inner, mergeSpecOverrides(col.overrides, overrides));
8885
+ return new ColumnOverriddenRecipe(col, overrides);
8162
8886
  }
8163
- }
8164
- /** Collects all column references (`column` and `rhs` fields) from filter leaves. */
8165
- function collectFilterSpecColumns(filter) {
8166
- return traverseFilterSpec(filter, {
8167
- leaf: (leaf) => {
8168
- const cols = [];
8169
- if ("column" in leaf && leaf.column !== void 0) cols.push(leaf.column);
8170
- if ("rhs" in leaf && leaf.rhs !== void 0) cols.push(leaf.rhs);
8171
- return cols;
8172
- },
8173
- and: (results) => results.flat(),
8174
- or: (results) => results.flat(),
8175
- not: (result) => result
8176
- });
8177
- }
8887
+ /**
8888
+ * Static counterpart to {@link wrap}: returns the resolution status of a
8889
+ * {@link ColumnOverriddenKey} without constructing the recipe. Overrides
8890
+ * do not add references — status collapses to the source's status.
8891
+ */
8892
+ static getStatusByKey(key, opts = {}) {
8893
+ return ColumnRecipe.getStatus(key.source, opts);
8894
+ }
8895
+ id;
8896
+ specCache;
8897
+ queryCache;
8898
+ dataStatusCache;
8899
+ constructor(inner, overrides) {
8900
+ this.inner = inner;
8901
+ this.overrides = overrides;
8902
+ this.id = createColumnOverriddenId({
8903
+ source: inner.id,
8904
+ specOverrides: overrides
8905
+ });
8906
+ }
8907
+ /** Wrapped recipe — exposed so external walkers can descend. */
8908
+ getInner() {
8909
+ return this.inner;
8910
+ }
8911
+ /**
8912
+ * `inner` owns all references; overrides are a pure spec patch and do not
8913
+ * introduce new column refs.
8914
+ */
8915
+ getReferencedIds() {
8916
+ return this.inner.getReferencedIds();
8917
+ }
8918
+ getDataStatus() {
8919
+ if (this.dataStatusCache === void 0) this.dataStatusCache = { value: this.inner.getDataStatus() };
8920
+ return this.dataStatusCache.value;
8921
+ }
8922
+ /**
8923
+ * Take the inner spec and overlay our overrides. `applySpecOverrides`
8924
+ * extracts the overrides from `this.id` itself.
8925
+ */
8926
+ getSpec() {
8927
+ if (this.specCache === void 0) this.specCache = { value: applySpecOverrides(this.inner.getSpec(), this.id) };
8928
+ return this.specCache.value;
8929
+ }
8930
+ /**
8931
+ * Emit a client-side `specOverride` node wrapping the inner query.
8932
+ *
8933
+ * This is a structural-but-collapsible node: it carries the override
8934
+ * patch through the query tree so consumers see the full recipe shape,
8935
+ * but it is collapsed at the host boundary (`resolvePColumn`) before
8936
+ * the query reaches pframe-engine. The engine never sees this node.
8937
+ *
8938
+ * Overrides do not change query topology — they are a pure spec patch.
8939
+ */
8940
+ getQuery() {
8941
+ if (this.queryCache === void 0) this.queryCache = { value: {
8942
+ type: "specOverride",
8943
+ input: rebrandLeafId(this.inner.getQuery(), this.inner.id, this.id),
8944
+ override: this.overrides
8945
+ } };
8946
+ return this.queryCache.value;
8947
+ }
8948
+ /**
8949
+ * Flat merge: new overrides merge with existing ones; `inner` is unchanged.
8950
+ * The result is an Overridden of the same depth.
8951
+ */
8952
+ withSpecs(overrides) {
8953
+ return ColumnOverriddenRecipe.wrap(this, overrides);
8954
+ }
8955
+ };
8178
8956
  //#endregion
8179
- //#region ../../../../sdk/model/dist/filters/converters/filterToQuery.js
8180
- /** Parses a CanonicalizedJson<PTableColumnId> string into a SpecQueryExpression reference. */
8181
- function resolveColumnRef(columnStr) {
8182
- const parsed = JSON.parse(columnStr);
8183
- return parsed.type === "axis" ? {
8184
- type: "axisRef",
8185
- value: parsed.id
8186
- } : {
8187
- type: "columnRef",
8188
- value: parsed.id
8189
- };
8190
- }
8191
- /** Converts a FilterSpec tree into a SpecQueryExpression. */
8192
- function filterSpecToSpecQueryExpr(filter) {
8193
- return traverseFilterSpec(filter, {
8194
- leaf: leafToSpecQueryExpr,
8195
- and: (inputs) => {
8196
- if (inputs.length === 0) throw new Error("AND filter requires at least one operand");
8197
- return {
8198
- type: "and",
8199
- input: inputs
8200
- };
8201
- },
8202
- or: (inputs) => {
8203
- if (inputs.length === 0) throw new Error("OR filter requires at least one operand");
8204
- return {
8205
- type: "or",
8206
- input: inputs
8207
- };
8208
- },
8209
- not: (input) => ({
8210
- type: "not",
8211
- input
8212
- })
8957
+ //#region ../../../../sdk/model/dist/columns/column_recipes/column_discovered_recipe.js
8958
+ /**
8959
+ * Recipe for columns identified by a {@link ColumnDiscoveredKey}: a base
8960
+ * column + a `path` of linkers + qualifications.
8961
+ *
8962
+ * Specifics:
8963
+ * - References more than one column (`column` + each `path[i].column`);
8964
+ * {@link getDataStatus} aggregates across the whole set.
8965
+ * - {@link getQuery} assembles the {@link SpecQuery} locally
8966
+ * ({@link buildSpecQuery}) — the hit is inlined as its own recipe's query
8967
+ * tree, linkers fold inside-out into nested `linkerJoin` nodes.
8968
+ * - {@link getSpec} passes through to the hit column's own spec: the linker
8969
+ * chain only enables co-indexing and does not remap the hit's `axesSpec`, so
8970
+ * no engine round-trip is needed (mirrors the Discovered branch of
8971
+ * `reconstructSpecFromId` on the host side).
8972
+ */
8973
+ var ColumnDiscoveredRecipe = class ColumnDiscoveredRecipe {
8974
+ key;
8975
+ columns;
8976
+ /**
8977
+ * Validate the key + resolve every referenced column via
8978
+ * {@link ColumnLazy.fromId} (which itself checks the leaf accessor reports
8979
+ * `hasData()`). Returns `undefined` if the key is malformed or any
8980
+ * referenced column is not yet spec-resolvable.
8981
+ *
8982
+ * Spec JSON is deserialized lazily on first {@link getSpec}/{@link getQuery}.
8983
+ */
8984
+ static fromKey(key, opts = {}) {
8985
+ const distilled = distillColumnDiscoveredKey(key);
8986
+ const columns = {};
8987
+ for (const uniId of referencedUniIdsOf(distilled)) {
8988
+ const recipe = Column(uniId, opts);
8989
+ if (recipe === void 0) return void 0;
8990
+ columns[uniId] = recipe;
8991
+ }
8992
+ return new ColumnDiscoveredRecipe(distilled, columns);
8993
+ }
8994
+ /**
8995
+ * Static counterpart to {@link fromKey}: returns the resolution status of
8996
+ * a {@link ColumnDiscoveredKey} without constructing the recipe. Worst-wins
8997
+ * across every referenced {@link ColumnUniversalId} (hit + path linkers),
8998
+ * each resolved via the top-level {@link ColumnRecipe.getStatus} dispatcher.
8999
+ */
9000
+ static getStatusByKey(key, opts = {}) {
9001
+ const distilled = distillColumnDiscoveredKey(key);
9002
+ let worst = "present";
9003
+ for (const uniId of referencedUniIdsOf(distilled)) {
9004
+ const s = ColumnRecipe.getStatus(uniId, opts);
9005
+ if (s === "absent") return "absent";
9006
+ if (s === "resolving") worst = "resolving";
9007
+ }
9008
+ return worst;
9009
+ }
9010
+ id;
9011
+ specCache;
9012
+ queryCache;
9013
+ dataStatusCache;
9014
+ constructor(key, columns) {
9015
+ this.key = key;
9016
+ this.columns = columns;
9017
+ this.id = stringifyColumnDiscoveredId(this.key);
9018
+ }
9019
+ /**
9020
+ * Universal id of the hit column (excluding path/qualifications on top).
9021
+ * May itself be a wrapped id (Overridden / Filtered / nested Discovered) —
9022
+ * preserves projections applied to the underlying column.
9023
+ */
9024
+ getHitId() {
9025
+ return this.key.column;
9026
+ }
9027
+ /**
9028
+ * Axis qualifications attached to the hit column itself — applied to the
9029
+ * outer-join entry that wraps this column at the consumer boundary.
9030
+ */
9031
+ getColumnQualifications() {
9032
+ return this.key.columnQualifications ?? [];
9033
+ }
9034
+ /**
9035
+ * Per-primary-column qualifications applied to the primary anchors on
9036
+ * this group's side. Keyed by the external primary column id.
9037
+ */
9038
+ getQueriesQualifications() {
9039
+ return this.key.queriesQualifications ?? {};
9040
+ }
9041
+ /**
9042
+ * Leaf {@link PObjectId}s the recipe physically depends on: hit column +
9043
+ * each linker in `path`, with any projection wrappers unwrapped down to
9044
+ * their underlying storage column. Used to track real-data dependencies
9045
+ * (e.g. {@link getDataStatus}), not projected identities.
9046
+ */
9047
+ getReferencedIds() {
9048
+ return [...new Set([...referencedUniIdsOf(this.key)].map((uniId) => extractPObjectId(uniId)))];
9049
+ }
9050
+ /**
9051
+ * Final spec of a discovered hit is just the hit column's own spec: the
9052
+ * linker chain only enables co-indexing, it does not remap the hit's
9053
+ * `axesSpec`, and axis qualifications live on the outer join entry (see
9054
+ * {@link buildSpecQuery}), not in the spec. So pass through to the hit
9055
+ * recipe's `getSpec()` — no engine round-trip needed. This mirrors the
9056
+ * Discovered branch of `reconstructSpecFromId` on the host side.
9057
+ */
9058
+ getSpec() {
9059
+ if (this.specCache === void 0) {
9060
+ const hit = this.columns[this.key.column] ?? throwError(`ColumnDiscoveredRecipe.getSpec: missing column ${this.key.column}`);
9061
+ this.specCache = { value: hit.getSpec() };
9062
+ }
9063
+ return this.specCache.value;
9064
+ }
9065
+ /** IR for building the final spec — see {@link buildSpecQuery}. */
9066
+ getQuery() {
9067
+ if (this.queryCache === void 0) this.queryCache = { value: this.buildSpecQuery() };
9068
+ return this.queryCache.value;
9069
+ }
9070
+ getDataStatus() {
9071
+ if (this.dataStatusCache === void 0) this.dataStatusCache = { value: Object.values(this.columns).reduce((worst, lazy) => {
9072
+ const s = lazy.getDataStatus();
9073
+ if (s === "absent" || worst === "absent") return "absent";
9074
+ if (s === "resolving") return "resolving";
9075
+ return worst;
9076
+ }, "present") };
9077
+ return this.dataStatusCache.value;
9078
+ }
9079
+ /**
9080
+ * Discovered + overrides → Overridden<Discovered>. Delegates to the
9081
+ * flat-merge factory of ColumnOverriddenRecipe; subsequent `withSpecs`
9082
+ * calls merge at the Overridden level (one wrapper, no nesting).
9083
+ */
9084
+ withSpecs(overrides) {
9085
+ return ColumnOverriddenRecipe.wrap(this, overrides);
9086
+ }
9087
+ /**
9088
+ * Assembles the SpecQuery for this discovered hit by folding `key.path`
9089
+ * around the hit column.
9090
+ *
9091
+ * The hit (innermost) is built by inlining `columns[key.column].getQuery()`
9092
+ * — recursing into the hit's own recipe — so wrapping layers on the hit
9093
+ * (Overridden / Filtered / nested Discovered) appear in the produced tree.
9094
+ * The previous `pframeSpec.buildQuery` path was a flat
9095
+ * {@link PObjectId}-only call and could not express that.
9096
+ *
9097
+ * Linkers stay as plain column references on the linker side of
9098
+ * `linkerJoin` (the spec node has no slot for a query-side linker); the
9099
+ * linker's recipe id is used directly. The frame registered in
9100
+ * {@link getSpec} carries each recipe's final spec under that same id, so
9101
+ * the engine resolves linker axes from the post-projection spec.
9102
+ *
9103
+ * Path ordering matches {@link ColumnDiscoveredKey.path}: `path[0]` is
9104
+ * outermost, `path[N-1]` is closest to the hit. We fold from the inside
9105
+ * out — innermost wrap first — so the result lines up with that convention.
9106
+ *
9107
+ * `columnQualifications` / `queriesQualifications` are not emitted here —
9108
+ * qualifications live on a {@link SpecQueryJoinEntry} wrapper (lost on
9109
+ * unwrapping to {@link SpecQuery}). External consumers apply them at the
9110
+ * outer join via {@link getColumnQualifications} / {@link getQueriesQualifications}.
9111
+ */
9112
+ buildSpecQuery() {
9113
+ const hitRecipe = this.columns[this.key.column] ?? throwError(`ColumnDiscoveredRecipe.buildSpecQuery: missing column ${this.key.column}`);
9114
+ let query = rebrandLeafId(hitRecipe.getQuery(), hitRecipe.id, this.id);
9115
+ const path = this.key.path ?? [];
9116
+ for (let i = path.length - 1; i >= 0; i--) query = {
9117
+ type: "linkerJoin",
9118
+ linker: (this.columns[path[i].column] ?? throwError(`ColumnDiscoveredRecipe.buildSpecQuery: missing linker column ${path[i].column}`)).getQuery(),
9119
+ secondary: [{ entry: query }]
9120
+ };
9121
+ return query;
9122
+ }
9123
+ };
9124
+ function referencedUniIdsOf(key) {
9125
+ return /* @__PURE__ */ new Set([key.column, ...(key.path ?? []).map((item) => item.column)]);
9126
+ }
9127
+ //#endregion
9128
+ //#region ../../../../sdk/model/dist/columns/column_recipes/column_filtered_recipe.js
9129
+ /**
9130
+ * Recipe wrapper that fixes the values of selected axes of an inner recipe
9131
+ * (axis slicing). The id is `FilteredPColumnId { source: inner.id,
9132
+ * axisFilters }`.
9133
+ *
9134
+ * Flat-merge invariant: `inner` is never another {@link ColumnFilteredRecipe}.
9135
+ * Repeated {@link wrap} concatenates `axisFilters` at the current level.
9136
+ *
9137
+ * Layering: `withSpecs` on a Filtered recipe yields
9138
+ * `Overridden<Filtered<inner>>` — Overridden is always the outermost layer.
9139
+ * The reverse layering (`Filtered<Overridden<...>>`) is not reachable via
9140
+ * the public interface.
9141
+ */
9142
+ var ColumnFilteredRecipe = class ColumnFilteredRecipe {
9143
+ inner;
9144
+ axisFilters;
9145
+ /**
9146
+ * Wrap any recipe with axis filters. If `inner` is already a
9147
+ * {@link ColumnFilteredRecipe}, concatenates `axisFilters` and returns a
9148
+ * new Filtered sharing the original `inner`.
9149
+ *
9150
+ * Validates every axis index against `inner.getSpec().axesSpec` at
9151
+ * construction — throws on an out-of-range index. After {@link wrap}, the
9152
+ * recipe is guaranteed to be spec-complete.
9153
+ *
9154
+ * Order semantics: new filters are appended after existing ones, preserving
9155
+ * their relative order. AND composition is otherwise commutative; order
9156
+ * only affects canonicalize/equality.
9157
+ */
9158
+ static wrap(col, axisFilters) {
9159
+ const combined = col instanceof ColumnFilteredRecipe ? [...col.axisFilters, ...axisFilters] : axisFilters;
9160
+ const base = col instanceof ColumnFilteredRecipe ? col.inner : col;
9161
+ if (combined.length === 0) throw new Error("ColumnFilteredRecipe.wrap: at least one axis filter must be provided");
9162
+ const innerSpec = base.getSpec();
9163
+ const fixed = /* @__PURE__ */ new Set();
9164
+ for (const [idx] of combined) {
9165
+ if (innerSpec.axesSpec[idx] === void 0) throw new Error(`ColumnFilteredRecipe.wrap: axis index ${idx} out of range (inner has ${innerSpec.axesSpec.length} axes)`);
9166
+ fixed.add(idx);
9167
+ }
9168
+ if (fixed.size >= innerSpec.axesSpec.length) throw new Error(`ColumnFilteredRecipe.wrap: filters fix all ${innerSpec.axesSpec.length} axes — at least one axis must remain`);
9169
+ return new ColumnFilteredRecipe(base, combined);
9170
+ }
9171
+ /**
9172
+ * Static counterpart to {@link wrap}: returns the resolution status of a
9173
+ * {@link ColumnFilteredKey} without constructing the recipe. Axis filters
9174
+ * do not add references — status collapses to the source's status.
9175
+ */
9176
+ static getStatusByKey(key, opts = {}) {
9177
+ if (typeof key.source !== "string") throw new Error("ColumnFilteredRecipe.getStatusByKey: filtered column with non-string source");
9178
+ return ColumnRecipe.getStatus(key.source, opts);
9179
+ }
9180
+ id;
9181
+ specCache;
9182
+ queryCache;
9183
+ dataStatusCache;
9184
+ constructor(inner, axisFilters) {
9185
+ this.inner = inner;
9186
+ this.axisFilters = axisFilters;
9187
+ this.id = stringifyColumnFilteredId({
9188
+ source: inner.id,
9189
+ axisFilters
9190
+ });
9191
+ }
9192
+ /** Wrapped recipe — exposed so external walkers can descend. */
9193
+ getInner() {
9194
+ return this.inner;
9195
+ }
9196
+ /** Axis filters do not introduce references to other columns. */
9197
+ getReferencedIds() {
9198
+ return this.inner.getReferencedIds();
9199
+ }
9200
+ getDataStatus() {
9201
+ if (this.dataStatusCache === void 0) this.dataStatusCache = { value: this.inner.getDataStatus() };
9202
+ return this.dataStatusCache.value;
9203
+ }
9204
+ /**
9205
+ * Final spec — the inner spec with fixed axes removed. Indices in
9206
+ * `axisFilters` are positional against `inner.getSpec().axesSpec` and were
9207
+ * validated at {@link wrap} time.
9208
+ */
9209
+ getSpec() {
9210
+ if (this.specCache === void 0) this.specCache = { value: applyAxisFilters(this.inner.getSpec(), this.axisFilters) };
9211
+ return this.specCache.value;
9212
+ }
9213
+ /**
9214
+ * Wraps the inner query in `SpecQuerySliceAxes`. Index→selector resolution
9215
+ * goes through the inner spec — guaranteed available since {@link wrap}
9216
+ * validated all indices at construction.
9217
+ */
9218
+ getQuery() {
9219
+ if (this.queryCache === void 0) {
9220
+ const innerQuery = this.inner.getQuery();
9221
+ if (this.axisFilters.length === 0) this.queryCache = { value: rebrandLeafId(innerQuery, this.inner.id, this.id) };
9222
+ else {
9223
+ const innerSpec = this.inner.getSpec();
9224
+ this.queryCache = { value: {
9225
+ type: "sliceAxes",
9226
+ input: rebrandLeafId(innerQuery, this.inner.id, this.id),
9227
+ axisFilters: this.axisFilters.map(([idx, value]) => ({
9228
+ axisSelector: toAxisSelector(innerSpec.axesSpec[idx]),
9229
+ constant: value
9230
+ }))
9231
+ } };
9232
+ }
9233
+ }
9234
+ return this.queryCache.value;
9235
+ }
9236
+ /**
9237
+ * Filtered + overrides → Overridden<Filtered>. Overridden is always the
9238
+ * outermost layer.
9239
+ */
9240
+ withSpecs(overrides) {
9241
+ return ColumnOverriddenRecipe.wrap(this, overrides);
9242
+ }
9243
+ };
9244
+ function toAxisSelector(axis) {
9245
+ const selector = {
9246
+ name: axis.name,
9247
+ type: axis.type
9248
+ };
9249
+ if (axis.domain !== void 0 && Object.keys(axis.domain).length > 0) selector.domain = axis.domain;
9250
+ if (axis.contextDomain !== void 0 && Object.keys(axis.contextDomain).length > 0) selector.contextDomain = axis.contextDomain;
9251
+ return selector;
9252
+ }
9253
+ //#endregion
9254
+ //#region ../../../../node_modules/.pnpm/lru-cache@11.2.2/node_modules/lru-cache/dist/esm/index.js
9255
+ /**
9256
+ * @module LRUCache
9257
+ */
9258
+ const defaultPerf = typeof performance === "object" && performance && typeof performance.now === "function" ? performance : Date;
9259
+ const warned = /* @__PURE__ */ new Set();
9260
+ /* c8 ignore start */
9261
+ const PROCESS = typeof process === "object" && !!process ? process : {};
9262
+ /* c8 ignore start */
9263
+ const emitWarning = (msg, type, code, fn) => {
9264
+ typeof PROCESS.emitWarning === "function" ? PROCESS.emitWarning(msg, type, code, fn) : console.error(`[${code}] ${type}: ${msg}`);
9265
+ };
9266
+ let AC = globalThis.AbortController;
9267
+ let AS = globalThis.AbortSignal;
9268
+ /* c8 ignore start */
9269
+ if (typeof AC === "undefined") {
9270
+ AS = class AbortSignal {
9271
+ onabort;
9272
+ _onabort = [];
9273
+ reason;
9274
+ aborted = false;
9275
+ addEventListener(_, fn) {
9276
+ this._onabort.push(fn);
9277
+ }
9278
+ };
9279
+ AC = class AbortController {
9280
+ constructor() {
9281
+ warnACPolyfill();
9282
+ }
9283
+ signal = new AS();
9284
+ abort(reason) {
9285
+ if (this.signal.aborted) return;
9286
+ this.signal.reason = reason;
9287
+ this.signal.aborted = true;
9288
+ for (const fn of this.signal._onabort) fn(reason);
9289
+ this.signal.onabort?.(reason);
9290
+ }
9291
+ };
9292
+ let printACPolyfillWarning = PROCESS.env?.LRU_CACHE_IGNORE_AC_WARNING !== "1";
9293
+ const warnACPolyfill = () => {
9294
+ if (!printACPolyfillWarning) return;
9295
+ printACPolyfillWarning = false;
9296
+ emitWarning("AbortController is not defined. If using lru-cache in node 14, load an AbortController polyfill from the `node-abort-controller` package. A minimal polyfill is provided for use by LRUCache.fetch(), but it should not be relied upon in other contexts (eg, passing it to other APIs that use AbortController/AbortSignal might have undesirable effects). You may disable this with LRU_CACHE_IGNORE_AC_WARNING=1 in the env.", "NO_ABORT_CONTROLLER", "ENOTSUP", warnACPolyfill);
9297
+ };
9298
+ }
9299
+ /* c8 ignore stop */
9300
+ const shouldWarn = (code) => !warned.has(code);
9301
+ const isPosInt = (n) => n && n === Math.floor(n) && n > 0 && isFinite(n);
9302
+ /* c8 ignore start */
9303
+ const getUintArray = (max) => !isPosInt(max) ? null : max <= Math.pow(2, 8) ? Uint8Array : max <= Math.pow(2, 16) ? Uint16Array : max <= Math.pow(2, 32) ? Uint32Array : max <= Number.MAX_SAFE_INTEGER ? ZeroArray : null;
9304
+ /* c8 ignore stop */
9305
+ var ZeroArray = class extends Array {
9306
+ constructor(size) {
9307
+ super(size);
9308
+ this.fill(0);
9309
+ }
9310
+ };
9311
+ var Stack = class Stack {
9312
+ heap;
9313
+ length;
9314
+ static #constructing = false;
9315
+ static create(max) {
9316
+ const HeapCls = getUintArray(max);
9317
+ if (!HeapCls) return [];
9318
+ Stack.#constructing = true;
9319
+ const s = new Stack(max, HeapCls);
9320
+ Stack.#constructing = false;
9321
+ return s;
9322
+ }
9323
+ constructor(max, HeapCls) {
9324
+ /* c8 ignore start */
9325
+ if (!Stack.#constructing) throw new TypeError("instantiate Stack using Stack.create(n)");
9326
+ /* c8 ignore stop */
9327
+ this.heap = new HeapCls(max);
9328
+ this.length = 0;
9329
+ }
9330
+ push(n) {
9331
+ this.heap[this.length++] = n;
9332
+ }
9333
+ pop() {
9334
+ return this.heap[--this.length];
9335
+ }
9336
+ };
9337
+ /**
9338
+ * Default export, the thing you're using this module to get.
9339
+ *
9340
+ * The `K` and `V` types define the key and value types, respectively. The
9341
+ * optional `FC` type defines the type of the `context` object passed to
9342
+ * `cache.fetch()` and `cache.memo()`.
9343
+ *
9344
+ * Keys and values **must not** be `null` or `undefined`.
9345
+ *
9346
+ * All properties from the options object (with the exception of `max`,
9347
+ * `maxSize`, `fetchMethod`, `memoMethod`, `dispose` and `disposeAfter`) are
9348
+ * added as normal public members. (The listed options are read-only getters.)
9349
+ *
9350
+ * Changing any of these will alter the defaults for subsequent method calls.
9351
+ */
9352
+ var LRUCache = class LRUCache {
9353
+ #max;
9354
+ #maxSize;
9355
+ #dispose;
9356
+ #onInsert;
9357
+ #disposeAfter;
9358
+ #fetchMethod;
9359
+ #memoMethod;
9360
+ #perf;
9361
+ /**
9362
+ * {@link LRUCache.OptionsBase.perf}
9363
+ */
9364
+ get perf() {
9365
+ return this.#perf;
9366
+ }
9367
+ /**
9368
+ * {@link LRUCache.OptionsBase.ttl}
9369
+ */
9370
+ ttl;
9371
+ /**
9372
+ * {@link LRUCache.OptionsBase.ttlResolution}
9373
+ */
9374
+ ttlResolution;
9375
+ /**
9376
+ * {@link LRUCache.OptionsBase.ttlAutopurge}
9377
+ */
9378
+ ttlAutopurge;
9379
+ /**
9380
+ * {@link LRUCache.OptionsBase.updateAgeOnGet}
9381
+ */
9382
+ updateAgeOnGet;
9383
+ /**
9384
+ * {@link LRUCache.OptionsBase.updateAgeOnHas}
9385
+ */
9386
+ updateAgeOnHas;
9387
+ /**
9388
+ * {@link LRUCache.OptionsBase.allowStale}
9389
+ */
9390
+ allowStale;
9391
+ /**
9392
+ * {@link LRUCache.OptionsBase.noDisposeOnSet}
9393
+ */
9394
+ noDisposeOnSet;
9395
+ /**
9396
+ * {@link LRUCache.OptionsBase.noUpdateTTL}
9397
+ */
9398
+ noUpdateTTL;
9399
+ /**
9400
+ * {@link LRUCache.OptionsBase.maxEntrySize}
9401
+ */
9402
+ maxEntrySize;
9403
+ /**
9404
+ * {@link LRUCache.OptionsBase.sizeCalculation}
9405
+ */
9406
+ sizeCalculation;
9407
+ /**
9408
+ * {@link LRUCache.OptionsBase.noDeleteOnFetchRejection}
9409
+ */
9410
+ noDeleteOnFetchRejection;
9411
+ /**
9412
+ * {@link LRUCache.OptionsBase.noDeleteOnStaleGet}
9413
+ */
9414
+ noDeleteOnStaleGet;
9415
+ /**
9416
+ * {@link LRUCache.OptionsBase.allowStaleOnFetchAbort}
9417
+ */
9418
+ allowStaleOnFetchAbort;
9419
+ /**
9420
+ * {@link LRUCache.OptionsBase.allowStaleOnFetchRejection}
9421
+ */
9422
+ allowStaleOnFetchRejection;
9423
+ /**
9424
+ * {@link LRUCache.OptionsBase.ignoreFetchAbort}
9425
+ */
9426
+ ignoreFetchAbort;
9427
+ #size;
9428
+ #calculatedSize;
9429
+ #keyMap;
9430
+ #keyList;
9431
+ #valList;
9432
+ #next;
9433
+ #prev;
9434
+ #head;
9435
+ #tail;
9436
+ #free;
9437
+ #disposed;
9438
+ #sizes;
9439
+ #starts;
9440
+ #ttls;
9441
+ #hasDispose;
9442
+ #hasFetchMethod;
9443
+ #hasDisposeAfter;
9444
+ #hasOnInsert;
9445
+ /**
9446
+ * Do not call this method unless you need to inspect the
9447
+ * inner workings of the cache. If anything returned by this
9448
+ * object is modified in any way, strange breakage may occur.
9449
+ *
9450
+ * These fields are private for a reason!
9451
+ *
9452
+ * @internal
9453
+ */
9454
+ static unsafeExposeInternals(c) {
9455
+ return {
9456
+ starts: c.#starts,
9457
+ ttls: c.#ttls,
9458
+ sizes: c.#sizes,
9459
+ keyMap: c.#keyMap,
9460
+ keyList: c.#keyList,
9461
+ valList: c.#valList,
9462
+ next: c.#next,
9463
+ prev: c.#prev,
9464
+ get head() {
9465
+ return c.#head;
9466
+ },
9467
+ get tail() {
9468
+ return c.#tail;
9469
+ },
9470
+ free: c.#free,
9471
+ isBackgroundFetch: (p) => c.#isBackgroundFetch(p),
9472
+ backgroundFetch: (k, index, options, context) => c.#backgroundFetch(k, index, options, context),
9473
+ moveToTail: (index) => c.#moveToTail(index),
9474
+ indexes: (options) => c.#indexes(options),
9475
+ rindexes: (options) => c.#rindexes(options),
9476
+ isStale: (index) => c.#isStale(index)
9477
+ };
9478
+ }
9479
+ /**
9480
+ * {@link LRUCache.OptionsBase.max} (read-only)
9481
+ */
9482
+ get max() {
9483
+ return this.#max;
9484
+ }
9485
+ /**
9486
+ * {@link LRUCache.OptionsBase.maxSize} (read-only)
9487
+ */
9488
+ get maxSize() {
9489
+ return this.#maxSize;
9490
+ }
9491
+ /**
9492
+ * The total computed size of items in the cache (read-only)
9493
+ */
9494
+ get calculatedSize() {
9495
+ return this.#calculatedSize;
9496
+ }
9497
+ /**
9498
+ * The number of items stored in the cache (read-only)
9499
+ */
9500
+ get size() {
9501
+ return this.#size;
9502
+ }
9503
+ /**
9504
+ * {@link LRUCache.OptionsBase.fetchMethod} (read-only)
9505
+ */
9506
+ get fetchMethod() {
9507
+ return this.#fetchMethod;
9508
+ }
9509
+ get memoMethod() {
9510
+ return this.#memoMethod;
9511
+ }
9512
+ /**
9513
+ * {@link LRUCache.OptionsBase.dispose} (read-only)
9514
+ */
9515
+ get dispose() {
9516
+ return this.#dispose;
9517
+ }
9518
+ /**
9519
+ * {@link LRUCache.OptionsBase.onInsert} (read-only)
9520
+ */
9521
+ get onInsert() {
9522
+ return this.#onInsert;
9523
+ }
9524
+ /**
9525
+ * {@link LRUCache.OptionsBase.disposeAfter} (read-only)
9526
+ */
9527
+ get disposeAfter() {
9528
+ return this.#disposeAfter;
9529
+ }
9530
+ constructor(options) {
9531
+ const { max = 0, ttl, ttlResolution = 1, ttlAutopurge, updateAgeOnGet, updateAgeOnHas, allowStale, dispose, onInsert, disposeAfter, noDisposeOnSet, noUpdateTTL, maxSize = 0, maxEntrySize = 0, sizeCalculation, fetchMethod, memoMethod, noDeleteOnFetchRejection, noDeleteOnStaleGet, allowStaleOnFetchRejection, allowStaleOnFetchAbort, ignoreFetchAbort, perf } = options;
9532
+ if (perf !== void 0) {
9533
+ if (typeof perf?.now !== "function") throw new TypeError("perf option must have a now() method if specified");
9534
+ }
9535
+ this.#perf = perf ?? defaultPerf;
9536
+ if (max !== 0 && !isPosInt(max)) throw new TypeError("max option must be a nonnegative integer");
9537
+ const UintArray = max ? getUintArray(max) : Array;
9538
+ if (!UintArray) throw new Error("invalid max value: " + max);
9539
+ this.#max = max;
9540
+ this.#maxSize = maxSize;
9541
+ this.maxEntrySize = maxEntrySize || this.#maxSize;
9542
+ this.sizeCalculation = sizeCalculation;
9543
+ if (this.sizeCalculation) {
9544
+ if (!this.#maxSize && !this.maxEntrySize) throw new TypeError("cannot set sizeCalculation without setting maxSize or maxEntrySize");
9545
+ if (typeof this.sizeCalculation !== "function") throw new TypeError("sizeCalculation set to non-function");
9546
+ }
9547
+ if (memoMethod !== void 0 && typeof memoMethod !== "function") throw new TypeError("memoMethod must be a function if defined");
9548
+ this.#memoMethod = memoMethod;
9549
+ if (fetchMethod !== void 0 && typeof fetchMethod !== "function") throw new TypeError("fetchMethod must be a function if specified");
9550
+ this.#fetchMethod = fetchMethod;
9551
+ this.#hasFetchMethod = !!fetchMethod;
9552
+ this.#keyMap = /* @__PURE__ */ new Map();
9553
+ this.#keyList = new Array(max).fill(void 0);
9554
+ this.#valList = new Array(max).fill(void 0);
9555
+ this.#next = new UintArray(max);
9556
+ this.#prev = new UintArray(max);
9557
+ this.#head = 0;
9558
+ this.#tail = 0;
9559
+ this.#free = Stack.create(max);
9560
+ this.#size = 0;
9561
+ this.#calculatedSize = 0;
9562
+ if (typeof dispose === "function") this.#dispose = dispose;
9563
+ if (typeof onInsert === "function") this.#onInsert = onInsert;
9564
+ if (typeof disposeAfter === "function") {
9565
+ this.#disposeAfter = disposeAfter;
9566
+ this.#disposed = [];
9567
+ } else {
9568
+ this.#disposeAfter = void 0;
9569
+ this.#disposed = void 0;
9570
+ }
9571
+ this.#hasDispose = !!this.#dispose;
9572
+ this.#hasOnInsert = !!this.#onInsert;
9573
+ this.#hasDisposeAfter = !!this.#disposeAfter;
9574
+ this.noDisposeOnSet = !!noDisposeOnSet;
9575
+ this.noUpdateTTL = !!noUpdateTTL;
9576
+ this.noDeleteOnFetchRejection = !!noDeleteOnFetchRejection;
9577
+ this.allowStaleOnFetchRejection = !!allowStaleOnFetchRejection;
9578
+ this.allowStaleOnFetchAbort = !!allowStaleOnFetchAbort;
9579
+ this.ignoreFetchAbort = !!ignoreFetchAbort;
9580
+ if (this.maxEntrySize !== 0) {
9581
+ if (this.#maxSize !== 0) {
9582
+ if (!isPosInt(this.#maxSize)) throw new TypeError("maxSize must be a positive integer if specified");
9583
+ }
9584
+ if (!isPosInt(this.maxEntrySize)) throw new TypeError("maxEntrySize must be a positive integer if specified");
9585
+ this.#initializeSizeTracking();
9586
+ }
9587
+ this.allowStale = !!allowStale;
9588
+ this.noDeleteOnStaleGet = !!noDeleteOnStaleGet;
9589
+ this.updateAgeOnGet = !!updateAgeOnGet;
9590
+ this.updateAgeOnHas = !!updateAgeOnHas;
9591
+ this.ttlResolution = isPosInt(ttlResolution) || ttlResolution === 0 ? ttlResolution : 1;
9592
+ this.ttlAutopurge = !!ttlAutopurge;
9593
+ this.ttl = ttl || 0;
9594
+ if (this.ttl) {
9595
+ if (!isPosInt(this.ttl)) throw new TypeError("ttl must be a positive integer if specified");
9596
+ this.#initializeTTLTracking();
9597
+ }
9598
+ if (this.#max === 0 && this.ttl === 0 && this.#maxSize === 0) throw new TypeError("At least one of max, maxSize, or ttl is required");
9599
+ if (!this.ttlAutopurge && !this.#max && !this.#maxSize) {
9600
+ const code = "LRU_CACHE_UNBOUNDED";
9601
+ if (shouldWarn(code)) {
9602
+ warned.add(code);
9603
+ emitWarning("TTL caching without ttlAutopurge, max, or maxSize can result in unbounded memory consumption.", "UnboundedCacheWarning", code, LRUCache);
9604
+ }
9605
+ }
9606
+ }
9607
+ /**
9608
+ * Return the number of ms left in the item's TTL. If item is not in cache,
9609
+ * returns `0`. Returns `Infinity` if item is in cache without a defined TTL.
9610
+ */
9611
+ getRemainingTTL(key) {
9612
+ return this.#keyMap.has(key) ? Infinity : 0;
9613
+ }
9614
+ #initializeTTLTracking() {
9615
+ const ttls = new ZeroArray(this.#max);
9616
+ const starts = new ZeroArray(this.#max);
9617
+ this.#ttls = ttls;
9618
+ this.#starts = starts;
9619
+ this.#setItemTTL = (index, ttl, start = this.#perf.now()) => {
9620
+ starts[index] = ttl !== 0 ? start : 0;
9621
+ ttls[index] = ttl;
9622
+ if (ttl !== 0 && this.ttlAutopurge) {
9623
+ const t = setTimeout(() => {
9624
+ if (this.#isStale(index)) this.#delete(this.#keyList[index], "expire");
9625
+ }, ttl + 1);
9626
+ /* c8 ignore start */
9627
+ if (t.unref) t.unref();
9628
+ }
9629
+ };
9630
+ this.#updateItemAge = (index) => {
9631
+ starts[index] = ttls[index] !== 0 ? this.#perf.now() : 0;
9632
+ };
9633
+ this.#statusTTL = (status, index) => {
9634
+ if (ttls[index]) {
9635
+ const ttl = ttls[index];
9636
+ const start = starts[index];
9637
+ /* c8 ignore next */
9638
+ if (!ttl || !start) return;
9639
+ status.ttl = ttl;
9640
+ status.start = start;
9641
+ status.now = cachedNow || getNow();
9642
+ status.remainingTTL = ttl - (status.now - start);
9643
+ }
9644
+ };
9645
+ let cachedNow = 0;
9646
+ const getNow = () => {
9647
+ const n = this.#perf.now();
9648
+ if (this.ttlResolution > 0) {
9649
+ cachedNow = n;
9650
+ const t = setTimeout(() => cachedNow = 0, this.ttlResolution);
9651
+ /* c8 ignore start */
9652
+ if (t.unref) t.unref();
9653
+ }
9654
+ return n;
9655
+ };
9656
+ this.getRemainingTTL = (key) => {
9657
+ const index = this.#keyMap.get(key);
9658
+ if (index === void 0) return 0;
9659
+ const ttl = ttls[index];
9660
+ const start = starts[index];
9661
+ if (!ttl || !start) return Infinity;
9662
+ return ttl - ((cachedNow || getNow()) - start);
9663
+ };
9664
+ this.#isStale = (index) => {
9665
+ const s = starts[index];
9666
+ const t = ttls[index];
9667
+ return !!t && !!s && (cachedNow || getNow()) - s > t;
9668
+ };
9669
+ }
9670
+ #updateItemAge = () => {};
9671
+ #statusTTL = () => {};
9672
+ #setItemTTL = () => {};
9673
+ /* c8 ignore stop */
9674
+ #isStale = () => false;
9675
+ #initializeSizeTracking() {
9676
+ const sizes = new ZeroArray(this.#max);
9677
+ this.#calculatedSize = 0;
9678
+ this.#sizes = sizes;
9679
+ this.#removeItemSize = (index) => {
9680
+ this.#calculatedSize -= sizes[index];
9681
+ sizes[index] = 0;
9682
+ };
9683
+ this.#requireSize = (k, v, size, sizeCalculation) => {
9684
+ if (this.#isBackgroundFetch(v)) return 0;
9685
+ if (!isPosInt(size)) if (sizeCalculation) {
9686
+ if (typeof sizeCalculation !== "function") throw new TypeError("sizeCalculation must be a function");
9687
+ size = sizeCalculation(v, k);
9688
+ if (!isPosInt(size)) throw new TypeError("sizeCalculation return invalid (expect positive integer)");
9689
+ } else throw new TypeError("invalid size value (must be positive integer). When maxSize or maxEntrySize is used, sizeCalculation or size must be set.");
9690
+ return size;
9691
+ };
9692
+ this.#addItemSize = (index, size, status) => {
9693
+ sizes[index] = size;
9694
+ if (this.#maxSize) {
9695
+ const maxSize = this.#maxSize - sizes[index];
9696
+ while (this.#calculatedSize > maxSize) this.#evict(true);
9697
+ }
9698
+ this.#calculatedSize += sizes[index];
9699
+ if (status) {
9700
+ status.entrySize = size;
9701
+ status.totalCalculatedSize = this.#calculatedSize;
9702
+ }
9703
+ };
9704
+ }
9705
+ #removeItemSize = (_i) => {};
9706
+ #addItemSize = (_i, _s, _st) => {};
9707
+ #requireSize = (_k, _v, size, sizeCalculation) => {
9708
+ if (size || sizeCalculation) throw new TypeError("cannot set size without setting maxSize or maxEntrySize on cache");
9709
+ return 0;
9710
+ };
9711
+ *#indexes({ allowStale = this.allowStale } = {}) {
9712
+ if (this.#size) for (let i = this.#tail;;) {
9713
+ if (!this.#isValidIndex(i)) break;
9714
+ if (allowStale || !this.#isStale(i)) yield i;
9715
+ if (i === this.#head) break;
9716
+ else i = this.#prev[i];
9717
+ }
9718
+ }
9719
+ *#rindexes({ allowStale = this.allowStale } = {}) {
9720
+ if (this.#size) for (let i = this.#head;;) {
9721
+ if (!this.#isValidIndex(i)) break;
9722
+ if (allowStale || !this.#isStale(i)) yield i;
9723
+ if (i === this.#tail) break;
9724
+ else i = this.#next[i];
9725
+ }
9726
+ }
9727
+ #isValidIndex(index) {
9728
+ return index !== void 0 && this.#keyMap.get(this.#keyList[index]) === index;
9729
+ }
9730
+ /**
9731
+ * Return a generator yielding `[key, value]` pairs,
9732
+ * in order from most recently used to least recently used.
9733
+ */
9734
+ *entries() {
9735
+ for (const i of this.#indexes()) if (this.#valList[i] !== void 0 && this.#keyList[i] !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) yield [this.#keyList[i], this.#valList[i]];
9736
+ }
9737
+ /**
9738
+ * Inverse order version of {@link LRUCache.entries}
9739
+ *
9740
+ * Return a generator yielding `[key, value]` pairs,
9741
+ * in order from least recently used to most recently used.
9742
+ */
9743
+ *rentries() {
9744
+ for (const i of this.#rindexes()) if (this.#valList[i] !== void 0 && this.#keyList[i] !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) yield [this.#keyList[i], this.#valList[i]];
9745
+ }
9746
+ /**
9747
+ * Return a generator yielding the keys in the cache,
9748
+ * in order from most recently used to least recently used.
9749
+ */
9750
+ *keys() {
9751
+ for (const i of this.#indexes()) {
9752
+ const k = this.#keyList[i];
9753
+ if (k !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) yield k;
9754
+ }
9755
+ }
9756
+ /**
9757
+ * Inverse order version of {@link LRUCache.keys}
9758
+ *
9759
+ * Return a generator yielding the keys in the cache,
9760
+ * in order from least recently used to most recently used.
9761
+ */
9762
+ *rkeys() {
9763
+ for (const i of this.#rindexes()) {
9764
+ const k = this.#keyList[i];
9765
+ if (k !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) yield k;
9766
+ }
9767
+ }
9768
+ /**
9769
+ * Return a generator yielding the values in the cache,
9770
+ * in order from most recently used to least recently used.
9771
+ */
9772
+ *values() {
9773
+ for (const i of this.#indexes()) if (this.#valList[i] !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) yield this.#valList[i];
9774
+ }
9775
+ /**
9776
+ * Inverse order version of {@link LRUCache.values}
9777
+ *
9778
+ * Return a generator yielding the values in the cache,
9779
+ * in order from least recently used to most recently used.
9780
+ */
9781
+ *rvalues() {
9782
+ for (const i of this.#rindexes()) if (this.#valList[i] !== void 0 && !this.#isBackgroundFetch(this.#valList[i])) yield this.#valList[i];
9783
+ }
9784
+ /**
9785
+ * Iterating over the cache itself yields the same results as
9786
+ * {@link LRUCache.entries}
9787
+ */
9788
+ [Symbol.iterator]() {
9789
+ return this.entries();
9790
+ }
9791
+ /**
9792
+ * A String value that is used in the creation of the default string
9793
+ * description of an object. Called by the built-in method
9794
+ * `Object.prototype.toString`.
9795
+ */
9796
+ [Symbol.toStringTag] = "LRUCache";
9797
+ /**
9798
+ * Find a value for which the supplied fn method returns a truthy value,
9799
+ * similar to `Array.find()`. fn is called as `fn(value, key, cache)`.
9800
+ */
9801
+ find(fn, getOptions = {}) {
9802
+ for (const i of this.#indexes()) {
9803
+ const v = this.#valList[i];
9804
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
9805
+ if (value === void 0) continue;
9806
+ if (fn(value, this.#keyList[i], this)) return this.get(this.#keyList[i], getOptions);
9807
+ }
9808
+ }
9809
+ /**
9810
+ * Call the supplied function on each item in the cache, in order from most
9811
+ * recently used to least recently used.
9812
+ *
9813
+ * `fn` is called as `fn(value, key, cache)`.
9814
+ *
9815
+ * If `thisp` is provided, function will be called in the `this`-context of
9816
+ * the provided object, or the cache if no `thisp` object is provided.
9817
+ *
9818
+ * Does not update age or recenty of use, or iterate over stale values.
9819
+ */
9820
+ forEach(fn, thisp = this) {
9821
+ for (const i of this.#indexes()) {
9822
+ const v = this.#valList[i];
9823
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
9824
+ if (value === void 0) continue;
9825
+ fn.call(thisp, value, this.#keyList[i], this);
9826
+ }
9827
+ }
9828
+ /**
9829
+ * The same as {@link LRUCache.forEach} but items are iterated over in
9830
+ * reverse order. (ie, less recently used items are iterated over first.)
9831
+ */
9832
+ rforEach(fn, thisp = this) {
9833
+ for (const i of this.#rindexes()) {
9834
+ const v = this.#valList[i];
9835
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
9836
+ if (value === void 0) continue;
9837
+ fn.call(thisp, value, this.#keyList[i], this);
9838
+ }
9839
+ }
9840
+ /**
9841
+ * Delete any stale entries. Returns true if anything was removed,
9842
+ * false otherwise.
9843
+ */
9844
+ purgeStale() {
9845
+ let deleted = false;
9846
+ for (const i of this.#rindexes({ allowStale: true })) if (this.#isStale(i)) {
9847
+ this.#delete(this.#keyList[i], "expire");
9848
+ deleted = true;
9849
+ }
9850
+ return deleted;
9851
+ }
9852
+ /**
9853
+ * Get the extended info about a given entry, to get its value, size, and
9854
+ * TTL info simultaneously. Returns `undefined` if the key is not present.
9855
+ *
9856
+ * Unlike {@link LRUCache#dump}, which is designed to be portable and survive
9857
+ * serialization, the `start` value is always the current timestamp, and the
9858
+ * `ttl` is a calculated remaining time to live (negative if expired).
9859
+ *
9860
+ * Always returns stale values, if their info is found in the cache, so be
9861
+ * sure to check for expirations (ie, a negative {@link LRUCache.Entry#ttl})
9862
+ * if relevant.
9863
+ */
9864
+ info(key) {
9865
+ const i = this.#keyMap.get(key);
9866
+ if (i === void 0) return void 0;
9867
+ const v = this.#valList[i];
9868
+ /* c8 ignore start - this isn't tested for the info function,
9869
+ * but it's the same logic as found in other places. */
9870
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
9871
+ if (value === void 0) return void 0;
9872
+ /* c8 ignore end */
9873
+ const entry = { value };
9874
+ if (this.#ttls && this.#starts) {
9875
+ const ttl = this.#ttls[i];
9876
+ const start = this.#starts[i];
9877
+ if (ttl && start) {
9878
+ entry.ttl = ttl - (this.#perf.now() - start);
9879
+ entry.start = Date.now();
9880
+ }
9881
+ }
9882
+ if (this.#sizes) entry.size = this.#sizes[i];
9883
+ return entry;
9884
+ }
9885
+ /**
9886
+ * Return an array of [key, {@link LRUCache.Entry}] tuples which can be
9887
+ * passed to {@link LRUCache#load}.
9888
+ *
9889
+ * The `start` fields are calculated relative to a portable `Date.now()`
9890
+ * timestamp, even if `performance.now()` is available.
9891
+ *
9892
+ * Stale entries are always included in the `dump`, even if
9893
+ * {@link LRUCache.OptionsBase.allowStale} is false.
9894
+ *
9895
+ * Note: this returns an actual array, not a generator, so it can be more
9896
+ * easily passed around.
9897
+ */
9898
+ dump() {
9899
+ const arr = [];
9900
+ for (const i of this.#indexes({ allowStale: true })) {
9901
+ const key = this.#keyList[i];
9902
+ const v = this.#valList[i];
9903
+ const value = this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
9904
+ if (value === void 0 || key === void 0) continue;
9905
+ const entry = { value };
9906
+ if (this.#ttls && this.#starts) {
9907
+ entry.ttl = this.#ttls[i];
9908
+ const age = this.#perf.now() - this.#starts[i];
9909
+ entry.start = Math.floor(Date.now() - age);
9910
+ }
9911
+ if (this.#sizes) entry.size = this.#sizes[i];
9912
+ arr.unshift([key, entry]);
9913
+ }
9914
+ return arr;
9915
+ }
9916
+ /**
9917
+ * Reset the cache and load in the items in entries in the order listed.
9918
+ *
9919
+ * The shape of the resulting cache may be different if the same options are
9920
+ * not used in both caches.
9921
+ *
9922
+ * The `start` fields are assumed to be calculated relative to a portable
9923
+ * `Date.now()` timestamp, even if `performance.now()` is available.
9924
+ */
9925
+ load(arr) {
9926
+ this.clear();
9927
+ for (const [key, entry] of arr) {
9928
+ if (entry.start) {
9929
+ const age = Date.now() - entry.start;
9930
+ entry.start = this.#perf.now() - age;
9931
+ }
9932
+ this.set(key, entry.value, entry);
9933
+ }
9934
+ }
9935
+ /**
9936
+ * Add a value to the cache.
9937
+ *
9938
+ * Note: if `undefined` is specified as a value, this is an alias for
9939
+ * {@link LRUCache#delete}
9940
+ *
9941
+ * Fields on the {@link LRUCache.SetOptions} options param will override
9942
+ * their corresponding values in the constructor options for the scope
9943
+ * of this single `set()` operation.
9944
+ *
9945
+ * If `start` is provided, then that will set the effective start
9946
+ * time for the TTL calculation. Note that this must be a previous
9947
+ * value of `performance.now()` if supported, or a previous value of
9948
+ * `Date.now()` if not.
9949
+ *
9950
+ * Options object may also include `size`, which will prevent
9951
+ * calling the `sizeCalculation` function and just use the specified
9952
+ * number if it is a positive integer, and `noDisposeOnSet` which
9953
+ * will prevent calling a `dispose` function in the case of
9954
+ * overwrites.
9955
+ *
9956
+ * If the `size` (or return value of `sizeCalculation`) for a given
9957
+ * entry is greater than `maxEntrySize`, then the item will not be
9958
+ * added to the cache.
9959
+ *
9960
+ * Will update the recency of the entry.
9961
+ *
9962
+ * If the value is `undefined`, then this is an alias for
9963
+ * `cache.delete(key)`. `undefined` is never stored in the cache.
9964
+ */
9965
+ set(k, v, setOptions = {}) {
9966
+ if (v === void 0) {
9967
+ this.delete(k);
9968
+ return this;
9969
+ }
9970
+ const { ttl = this.ttl, start, noDisposeOnSet = this.noDisposeOnSet, sizeCalculation = this.sizeCalculation, status } = setOptions;
9971
+ let { noUpdateTTL = this.noUpdateTTL } = setOptions;
9972
+ const size = this.#requireSize(k, v, setOptions.size || 0, sizeCalculation);
9973
+ if (this.maxEntrySize && size > this.maxEntrySize) {
9974
+ if (status) {
9975
+ status.set = "miss";
9976
+ status.maxEntrySizeExceeded = true;
9977
+ }
9978
+ this.#delete(k, "set");
9979
+ return this;
9980
+ }
9981
+ let index = this.#size === 0 ? void 0 : this.#keyMap.get(k);
9982
+ if (index === void 0) {
9983
+ index = this.#size === 0 ? this.#tail : this.#free.length !== 0 ? this.#free.pop() : this.#size === this.#max ? this.#evict(false) : this.#size;
9984
+ this.#keyList[index] = k;
9985
+ this.#valList[index] = v;
9986
+ this.#keyMap.set(k, index);
9987
+ this.#next[this.#tail] = index;
9988
+ this.#prev[index] = this.#tail;
9989
+ this.#tail = index;
9990
+ this.#size++;
9991
+ this.#addItemSize(index, size, status);
9992
+ if (status) status.set = "add";
9993
+ noUpdateTTL = false;
9994
+ if (this.#hasOnInsert) this.#onInsert?.(v, k, "add");
9995
+ } else {
9996
+ this.#moveToTail(index);
9997
+ const oldVal = this.#valList[index];
9998
+ if (v !== oldVal) {
9999
+ if (this.#hasFetchMethod && this.#isBackgroundFetch(oldVal)) {
10000
+ oldVal.__abortController.abort(/* @__PURE__ */ new Error("replaced"));
10001
+ const { __staleWhileFetching: s } = oldVal;
10002
+ if (s !== void 0 && !noDisposeOnSet) {
10003
+ if (this.#hasDispose) this.#dispose?.(s, k, "set");
10004
+ if (this.#hasDisposeAfter) this.#disposed?.push([
10005
+ s,
10006
+ k,
10007
+ "set"
10008
+ ]);
10009
+ }
10010
+ } else if (!noDisposeOnSet) {
10011
+ if (this.#hasDispose) this.#dispose?.(oldVal, k, "set");
10012
+ if (this.#hasDisposeAfter) this.#disposed?.push([
10013
+ oldVal,
10014
+ k,
10015
+ "set"
10016
+ ]);
10017
+ }
10018
+ this.#removeItemSize(index);
10019
+ this.#addItemSize(index, size, status);
10020
+ this.#valList[index] = v;
10021
+ if (status) {
10022
+ status.set = "replace";
10023
+ const oldValue = oldVal && this.#isBackgroundFetch(oldVal) ? oldVal.__staleWhileFetching : oldVal;
10024
+ if (oldValue !== void 0) status.oldValue = oldValue;
10025
+ }
10026
+ } else if (status) status.set = "update";
10027
+ if (this.#hasOnInsert) this.onInsert?.(v, k, v === oldVal ? "update" : "replace");
10028
+ }
10029
+ if (ttl !== 0 && !this.#ttls) this.#initializeTTLTracking();
10030
+ if (this.#ttls) {
10031
+ if (!noUpdateTTL) this.#setItemTTL(index, ttl, start);
10032
+ if (status) this.#statusTTL(status, index);
10033
+ }
10034
+ if (!noDisposeOnSet && this.#hasDisposeAfter && this.#disposed) {
10035
+ const dt = this.#disposed;
10036
+ let task;
10037
+ while (task = dt?.shift()) this.#disposeAfter?.(...task);
10038
+ }
10039
+ return this;
10040
+ }
10041
+ /**
10042
+ * Evict the least recently used item, returning its value or
10043
+ * `undefined` if cache is empty.
10044
+ */
10045
+ pop() {
10046
+ try {
10047
+ while (this.#size) {
10048
+ const val = this.#valList[this.#head];
10049
+ this.#evict(true);
10050
+ if (this.#isBackgroundFetch(val)) {
10051
+ if (val.__staleWhileFetching) return val.__staleWhileFetching;
10052
+ } else if (val !== void 0) return val;
10053
+ }
10054
+ } finally {
10055
+ if (this.#hasDisposeAfter && this.#disposed) {
10056
+ const dt = this.#disposed;
10057
+ let task;
10058
+ while (task = dt?.shift()) this.#disposeAfter?.(...task);
10059
+ }
10060
+ }
10061
+ }
10062
+ #evict(free) {
10063
+ const head = this.#head;
10064
+ const k = this.#keyList[head];
10065
+ const v = this.#valList[head];
10066
+ if (this.#hasFetchMethod && this.#isBackgroundFetch(v)) v.__abortController.abort(/* @__PURE__ */ new Error("evicted"));
10067
+ else if (this.#hasDispose || this.#hasDisposeAfter) {
10068
+ if (this.#hasDispose) this.#dispose?.(v, k, "evict");
10069
+ if (this.#hasDisposeAfter) this.#disposed?.push([
10070
+ v,
10071
+ k,
10072
+ "evict"
10073
+ ]);
10074
+ }
10075
+ this.#removeItemSize(head);
10076
+ if (free) {
10077
+ this.#keyList[head] = void 0;
10078
+ this.#valList[head] = void 0;
10079
+ this.#free.push(head);
10080
+ }
10081
+ if (this.#size === 1) {
10082
+ this.#head = this.#tail = 0;
10083
+ this.#free.length = 0;
10084
+ } else this.#head = this.#next[head];
10085
+ this.#keyMap.delete(k);
10086
+ this.#size--;
10087
+ return head;
10088
+ }
10089
+ /**
10090
+ * Check if a key is in the cache, without updating the recency of use.
10091
+ * Will return false if the item is stale, even though it is technically
10092
+ * in the cache.
10093
+ *
10094
+ * Check if a key is in the cache, without updating the recency of
10095
+ * use. Age is updated if {@link LRUCache.OptionsBase.updateAgeOnHas} is set
10096
+ * to `true` in either the options or the constructor.
10097
+ *
10098
+ * Will return `false` if the item is stale, even though it is technically in
10099
+ * the cache. The difference can be determined (if it matters) by using a
10100
+ * `status` argument, and inspecting the `has` field.
10101
+ *
10102
+ * Will not update item age unless
10103
+ * {@link LRUCache.OptionsBase.updateAgeOnHas} is set.
10104
+ */
10105
+ has(k, hasOptions = {}) {
10106
+ const { updateAgeOnHas = this.updateAgeOnHas, status } = hasOptions;
10107
+ const index = this.#keyMap.get(k);
10108
+ if (index !== void 0) {
10109
+ const v = this.#valList[index];
10110
+ if (this.#isBackgroundFetch(v) && v.__staleWhileFetching === void 0) return false;
10111
+ if (!this.#isStale(index)) {
10112
+ if (updateAgeOnHas) this.#updateItemAge(index);
10113
+ if (status) {
10114
+ status.has = "hit";
10115
+ this.#statusTTL(status, index);
10116
+ }
10117
+ return true;
10118
+ } else if (status) {
10119
+ status.has = "stale";
10120
+ this.#statusTTL(status, index);
10121
+ }
10122
+ } else if (status) status.has = "miss";
10123
+ return false;
10124
+ }
10125
+ /**
10126
+ * Like {@link LRUCache#get} but doesn't update recency or delete stale
10127
+ * items.
10128
+ *
10129
+ * Returns `undefined` if the item is stale, unless
10130
+ * {@link LRUCache.OptionsBase.allowStale} is set.
10131
+ */
10132
+ peek(k, peekOptions = {}) {
10133
+ const { allowStale = this.allowStale } = peekOptions;
10134
+ const index = this.#keyMap.get(k);
10135
+ if (index === void 0 || !allowStale && this.#isStale(index)) return;
10136
+ const v = this.#valList[index];
10137
+ return this.#isBackgroundFetch(v) ? v.__staleWhileFetching : v;
10138
+ }
10139
+ #backgroundFetch(k, index, options, context) {
10140
+ const v = index === void 0 ? void 0 : this.#valList[index];
10141
+ if (this.#isBackgroundFetch(v)) return v;
10142
+ const ac = new AC();
10143
+ const { signal } = options;
10144
+ signal?.addEventListener("abort", () => ac.abort(signal.reason), { signal: ac.signal });
10145
+ const fetchOpts = {
10146
+ signal: ac.signal,
10147
+ options,
10148
+ context
10149
+ };
10150
+ const cb = (v, updateCache = false) => {
10151
+ const { aborted } = ac.signal;
10152
+ const ignoreAbort = options.ignoreFetchAbort && v !== void 0;
10153
+ if (options.status) if (aborted && !updateCache) {
10154
+ options.status.fetchAborted = true;
10155
+ options.status.fetchError = ac.signal.reason;
10156
+ if (ignoreAbort) options.status.fetchAbortIgnored = true;
10157
+ } else options.status.fetchResolved = true;
10158
+ if (aborted && !ignoreAbort && !updateCache) return fetchFail(ac.signal.reason);
10159
+ const bf = p;
10160
+ const vl = this.#valList[index];
10161
+ if (vl === p || ignoreAbort && updateCache && vl === void 0) if (v === void 0) if (bf.__staleWhileFetching !== void 0) this.#valList[index] = bf.__staleWhileFetching;
10162
+ else this.#delete(k, "fetch");
10163
+ else {
10164
+ if (options.status) options.status.fetchUpdated = true;
10165
+ this.set(k, v, fetchOpts.options);
10166
+ }
10167
+ return v;
10168
+ };
10169
+ const eb = (er) => {
10170
+ if (options.status) {
10171
+ options.status.fetchRejected = true;
10172
+ options.status.fetchError = er;
10173
+ }
10174
+ return fetchFail(er);
10175
+ };
10176
+ const fetchFail = (er) => {
10177
+ const { aborted } = ac.signal;
10178
+ const allowStaleAborted = aborted && options.allowStaleOnFetchAbort;
10179
+ const allowStale = allowStaleAborted || options.allowStaleOnFetchRejection;
10180
+ const noDelete = allowStale || options.noDeleteOnFetchRejection;
10181
+ const bf = p;
10182
+ if (this.#valList[index] === p) {
10183
+ if (!noDelete || bf.__staleWhileFetching === void 0) this.#delete(k, "fetch");
10184
+ else if (!allowStaleAborted) this.#valList[index] = bf.__staleWhileFetching;
10185
+ }
10186
+ if (allowStale) {
10187
+ if (options.status && bf.__staleWhileFetching !== void 0) options.status.returnedStale = true;
10188
+ return bf.__staleWhileFetching;
10189
+ } else if (bf.__returned === bf) throw er;
10190
+ };
10191
+ const pcall = (res, rej) => {
10192
+ const fmp = this.#fetchMethod?.(k, v, fetchOpts);
10193
+ if (fmp && fmp instanceof Promise) fmp.then((v) => res(v === void 0 ? void 0 : v), rej);
10194
+ ac.signal.addEventListener("abort", () => {
10195
+ if (!options.ignoreFetchAbort || options.allowStaleOnFetchAbort) {
10196
+ res(void 0);
10197
+ if (options.allowStaleOnFetchAbort) res = (v) => cb(v, true);
10198
+ }
10199
+ });
10200
+ };
10201
+ if (options.status) options.status.fetchDispatched = true;
10202
+ const p = new Promise(pcall).then(cb, eb);
10203
+ const bf = Object.assign(p, {
10204
+ __abortController: ac,
10205
+ __staleWhileFetching: v,
10206
+ __returned: void 0
10207
+ });
10208
+ if (index === void 0) {
10209
+ this.set(k, bf, {
10210
+ ...fetchOpts.options,
10211
+ status: void 0
10212
+ });
10213
+ index = this.#keyMap.get(k);
10214
+ } else this.#valList[index] = bf;
10215
+ return bf;
10216
+ }
10217
+ #isBackgroundFetch(p) {
10218
+ if (!this.#hasFetchMethod) return false;
10219
+ const b = p;
10220
+ return !!b && b instanceof Promise && b.hasOwnProperty("__staleWhileFetching") && b.__abortController instanceof AC;
10221
+ }
10222
+ async fetch(k, fetchOptions = {}) {
10223
+ const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, ttl = this.ttl, noDisposeOnSet = this.noDisposeOnSet, size = 0, sizeCalculation = this.sizeCalculation, noUpdateTTL = this.noUpdateTTL, noDeleteOnFetchRejection = this.noDeleteOnFetchRejection, allowStaleOnFetchRejection = this.allowStaleOnFetchRejection, ignoreFetchAbort = this.ignoreFetchAbort, allowStaleOnFetchAbort = this.allowStaleOnFetchAbort, context, forceRefresh = false, status, signal } = fetchOptions;
10224
+ if (!this.#hasFetchMethod) {
10225
+ if (status) status.fetch = "get";
10226
+ return this.get(k, {
10227
+ allowStale,
10228
+ updateAgeOnGet,
10229
+ noDeleteOnStaleGet,
10230
+ status
10231
+ });
10232
+ }
10233
+ const options = {
10234
+ allowStale,
10235
+ updateAgeOnGet,
10236
+ noDeleteOnStaleGet,
10237
+ ttl,
10238
+ noDisposeOnSet,
10239
+ size,
10240
+ sizeCalculation,
10241
+ noUpdateTTL,
10242
+ noDeleteOnFetchRejection,
10243
+ allowStaleOnFetchRejection,
10244
+ allowStaleOnFetchAbort,
10245
+ ignoreFetchAbort,
10246
+ status,
10247
+ signal
10248
+ };
10249
+ let index = this.#keyMap.get(k);
10250
+ if (index === void 0) {
10251
+ if (status) status.fetch = "miss";
10252
+ const p = this.#backgroundFetch(k, index, options, context);
10253
+ return p.__returned = p;
10254
+ } else {
10255
+ const v = this.#valList[index];
10256
+ if (this.#isBackgroundFetch(v)) {
10257
+ const stale = allowStale && v.__staleWhileFetching !== void 0;
10258
+ if (status) {
10259
+ status.fetch = "inflight";
10260
+ if (stale) status.returnedStale = true;
10261
+ }
10262
+ return stale ? v.__staleWhileFetching : v.__returned = v;
10263
+ }
10264
+ const isStale = this.#isStale(index);
10265
+ if (!forceRefresh && !isStale) {
10266
+ if (status) status.fetch = "hit";
10267
+ this.#moveToTail(index);
10268
+ if (updateAgeOnGet) this.#updateItemAge(index);
10269
+ if (status) this.#statusTTL(status, index);
10270
+ return v;
10271
+ }
10272
+ const p = this.#backgroundFetch(k, index, options, context);
10273
+ const staleVal = p.__staleWhileFetching !== void 0 && allowStale;
10274
+ if (status) {
10275
+ status.fetch = isStale ? "stale" : "refresh";
10276
+ if (staleVal && isStale) status.returnedStale = true;
10277
+ }
10278
+ return staleVal ? p.__staleWhileFetching : p.__returned = p;
10279
+ }
10280
+ }
10281
+ async forceFetch(k, fetchOptions = {}) {
10282
+ const v = await this.fetch(k, fetchOptions);
10283
+ if (v === void 0) throw new Error("fetch() returned undefined");
10284
+ return v;
10285
+ }
10286
+ memo(k, memoOptions = {}) {
10287
+ const memoMethod = this.#memoMethod;
10288
+ if (!memoMethod) throw new Error("no memoMethod provided to constructor");
10289
+ const { context, forceRefresh, ...options } = memoOptions;
10290
+ const v = this.get(k, options);
10291
+ if (!forceRefresh && v !== void 0) return v;
10292
+ const vv = memoMethod(k, v, {
10293
+ options,
10294
+ context
10295
+ });
10296
+ this.set(k, vv, options);
10297
+ return vv;
10298
+ }
10299
+ /**
10300
+ * Return a value from the cache. Will update the recency of the cache
10301
+ * entry found.
10302
+ *
10303
+ * If the key is not found, get() will return `undefined`.
10304
+ */
10305
+ get(k, getOptions = {}) {
10306
+ const { allowStale = this.allowStale, updateAgeOnGet = this.updateAgeOnGet, noDeleteOnStaleGet = this.noDeleteOnStaleGet, status } = getOptions;
10307
+ const index = this.#keyMap.get(k);
10308
+ if (index !== void 0) {
10309
+ const value = this.#valList[index];
10310
+ const fetching = this.#isBackgroundFetch(value);
10311
+ if (status) this.#statusTTL(status, index);
10312
+ if (this.#isStale(index)) {
10313
+ if (status) status.get = "stale";
10314
+ if (!fetching) {
10315
+ if (!noDeleteOnStaleGet) this.#delete(k, "expire");
10316
+ if (status && allowStale) status.returnedStale = true;
10317
+ return allowStale ? value : void 0;
10318
+ } else {
10319
+ if (status && allowStale && value.__staleWhileFetching !== void 0) status.returnedStale = true;
10320
+ return allowStale ? value.__staleWhileFetching : void 0;
10321
+ }
10322
+ } else {
10323
+ if (status) status.get = "hit";
10324
+ if (fetching) return value.__staleWhileFetching;
10325
+ this.#moveToTail(index);
10326
+ if (updateAgeOnGet) this.#updateItemAge(index);
10327
+ return value;
10328
+ }
10329
+ } else if (status) status.get = "miss";
10330
+ }
10331
+ #connect(p, n) {
10332
+ this.#prev[n] = p;
10333
+ this.#next[p] = n;
10334
+ }
10335
+ #moveToTail(index) {
10336
+ if (index !== this.#tail) {
10337
+ if (index === this.#head) this.#head = this.#next[index];
10338
+ else this.#connect(this.#prev[index], this.#next[index]);
10339
+ this.#connect(this.#tail, index);
10340
+ this.#tail = index;
10341
+ }
10342
+ }
10343
+ /**
10344
+ * Deletes a key out of the cache.
10345
+ *
10346
+ * Returns true if the key was deleted, false otherwise.
10347
+ */
10348
+ delete(k) {
10349
+ return this.#delete(k, "delete");
10350
+ }
10351
+ #delete(k, reason) {
10352
+ let deleted = false;
10353
+ if (this.#size !== 0) {
10354
+ const index = this.#keyMap.get(k);
10355
+ if (index !== void 0) {
10356
+ deleted = true;
10357
+ if (this.#size === 1) this.#clear(reason);
10358
+ else {
10359
+ this.#removeItemSize(index);
10360
+ const v = this.#valList[index];
10361
+ if (this.#isBackgroundFetch(v)) v.__abortController.abort(/* @__PURE__ */ new Error("deleted"));
10362
+ else if (this.#hasDispose || this.#hasDisposeAfter) {
10363
+ if (this.#hasDispose) this.#dispose?.(v, k, reason);
10364
+ if (this.#hasDisposeAfter) this.#disposed?.push([
10365
+ v,
10366
+ k,
10367
+ reason
10368
+ ]);
10369
+ }
10370
+ this.#keyMap.delete(k);
10371
+ this.#keyList[index] = void 0;
10372
+ this.#valList[index] = void 0;
10373
+ if (index === this.#tail) this.#tail = this.#prev[index];
10374
+ else if (index === this.#head) this.#head = this.#next[index];
10375
+ else {
10376
+ const pi = this.#prev[index];
10377
+ this.#next[pi] = this.#next[index];
10378
+ const ni = this.#next[index];
10379
+ this.#prev[ni] = this.#prev[index];
10380
+ }
10381
+ this.#size--;
10382
+ this.#free.push(index);
10383
+ }
10384
+ }
10385
+ }
10386
+ if (this.#hasDisposeAfter && this.#disposed?.length) {
10387
+ const dt = this.#disposed;
10388
+ let task;
10389
+ while (task = dt?.shift()) this.#disposeAfter?.(...task);
10390
+ }
10391
+ return deleted;
10392
+ }
10393
+ /**
10394
+ * Clear the cache entirely, throwing away all values.
10395
+ */
10396
+ clear() {
10397
+ return this.#clear("delete");
10398
+ }
10399
+ #clear(reason) {
10400
+ for (const index of this.#rindexes({ allowStale: true })) {
10401
+ const v = this.#valList[index];
10402
+ if (this.#isBackgroundFetch(v)) v.__abortController.abort(/* @__PURE__ */ new Error("deleted"));
10403
+ else {
10404
+ const k = this.#keyList[index];
10405
+ if (this.#hasDispose) this.#dispose?.(v, k, reason);
10406
+ if (this.#hasDisposeAfter) this.#disposed?.push([
10407
+ v,
10408
+ k,
10409
+ reason
10410
+ ]);
10411
+ }
10412
+ }
10413
+ this.#keyMap.clear();
10414
+ this.#valList.fill(void 0);
10415
+ this.#keyList.fill(void 0);
10416
+ if (this.#ttls && this.#starts) {
10417
+ this.#ttls.fill(0);
10418
+ this.#starts.fill(0);
10419
+ }
10420
+ if (this.#sizes) this.#sizes.fill(0);
10421
+ this.#head = 0;
10422
+ this.#tail = 0;
10423
+ this.#free.length = 0;
10424
+ this.#calculatedSize = 0;
10425
+ this.#size = 0;
10426
+ if (this.#hasDisposeAfter && this.#disposed) {
10427
+ const dt = this.#disposed;
10428
+ let task;
10429
+ while (task = dt?.shift()) this.#disposeAfter?.(...task);
10430
+ }
10431
+ }
10432
+ };
10433
+ //#endregion
10434
+ //#region ../../../../sdk/model/dist/columns/column_providers/providers.js
10435
+ /**
10436
+ * Unified memoised factory dispatching to the right provider flavour by the
10437
+ * source shape:
10438
+ * - `TreeNodeAccessor` → {@link AccessorColumnsProvider}
10439
+ * (indexed by `root.handle`, walks the accessor tree once per root).
10440
+ * - `UpstreamBlockCtx<AccessorHandle>[]` (or omitted — pulled from the
10441
+ * ambient ctx) → {@link ResultPoolColumnsProvider}
10442
+ * (indexed by a content-hash of the block list).
10443
+ *
10444
+ * Both branches return cached instances within their LRU window, so repeated
10445
+ * calls with the same source skip re-walking / re-hashing.
10446
+ */
10447
+ function ColumnsProvider(source) {
10448
+ if (isNil(source) || Array.isArray(source)) return ResultPoolColumnsProvider(source);
10449
+ else if (source instanceof TreeNodeAccessor) return AccessorColumnsProvider(source);
10450
+ else throw new Error("Unknown columns provider source");
10451
+ }
10452
+ /**
10453
+ * Memoised constructor — same `root.handle` returns the same
10454
+ * {@link AccessorColumnsProviderImpl}, so `indexAccessorRoot` runs once per
10455
+ * accessor root (within the LRU window).
10456
+ */
10457
+ const _accessorProviderCache = new LRUCache({ max: 64 });
10458
+ function AccessorColumnsProvider(root) {
10459
+ let provider = _accessorProviderCache.get(root.handle);
10460
+ if (provider === void 0) {
10461
+ provider = new AccessorColumnsProviderImpl(root);
10462
+ _accessorProviderCache.set(root.handle, provider);
10463
+ }
10464
+ return provider;
10465
+ }
10466
+ /**
10467
+ * Memoised constructor — same `rawPool` content returns the same
10468
+ * {@link ResultPoolColumnsProviderImpl}, so `indexPoolBlock` runs once per
10469
+ * pool snapshot (within the LRU window).
10470
+ */
10471
+ const _resultPoolProviderCache = new LRUCache({ max: 4 });
10472
+ function ResultPoolColumnsProvider(rawPool = getCfgRenderCtx().getUpstreamBlockCtx()) {
10473
+ const key = hashRawPool(rawPool);
10474
+ let provider = _resultPoolProviderCache.get(key);
10475
+ if (provider === void 0) {
10476
+ provider = new ResultPoolColumnsProviderImpl(rawPool);
10477
+ _resultPoolProviderCache.set(key, provider);
10478
+ }
10479
+ return provider;
10480
+ }
10481
+ /**
10482
+ * Sandbox-specific provider over a {@link TreeNodeAccessor} root. Uses the
10483
+ * accessor's own `resolvePath` as the canonical root path for id construction,
10484
+ * and adds `getColumns()` returning {@link ColumnLazy}s on top of the generic
10485
+ * entries index.
10486
+ */
10487
+ var AccessorColumnsProviderImpl = class extends AccessorEntriesProvider {
10488
+ cachedColumns;
10489
+ constructor(root) {
10490
+ super(root, root.resolvePath);
10491
+ }
10492
+ getColumns() {
10493
+ if (this.cachedColumns !== void 0) return this.cachedColumns;
10494
+ return this.cachedColumns = Array.from(this.entries.values()).map((e) => ColumnLazyImpl.fromAccessor(e)).filter((v) => !isNil(v));
10495
+ }
10496
+ };
10497
+ /**
10498
+ * Sandbox-specific result-pool provider. Accepts the raw handle-shaped blocks
10499
+ * returned by {@link GlobalCfgRenderCtxMethods.getUpstreamBlockCtx} and
10500
+ * wraps them into {@link TreeNodeAccessor}s, then adds `getColumns()`.
10501
+ */
10502
+ var ResultPoolColumnsProviderImpl = class extends ResultPoolEntriesProvider {
10503
+ cachedColumns;
10504
+ constructor(rawPool = getCfgRenderCtx().getUpstreamBlockCtx()) {
10505
+ const blocks = rawPool.map((b) => ({
10506
+ blockId: b.blockId,
10507
+ prodCtx: b.prodCtx !== void 0 ? new TreeNodeAccessor(b.prodCtx, [b.blockId, "prodCtx"]) : void 0,
10508
+ stagingCtx: b.stagingCtx !== void 0 ? new TreeNodeAccessor(b.stagingCtx, [b.blockId, "stagingCtx"]) : void 0,
10509
+ prodIncomplete: b.prodIncomplete,
10510
+ stagingIncomplete: b.stagingIncomplete
10511
+ }));
10512
+ super(blocks);
10513
+ }
10514
+ getColumns() {
10515
+ if (this.cachedColumns !== void 0) return this.cachedColumns;
10516
+ return this.cachedColumns = Array.from(this.getPObjectEntries().values()).map((e) => ColumnLazyImpl.fromAccessor(e)).filter((v) => !isNil(v));
10517
+ }
10518
+ };
10519
+ function hashRawPool(rawPool) {
10520
+ return rawPool.map((b) => `${b.blockId}\x1f${b.prodCtx ?? ""}\x1f${b.stagingCtx ?? ""}\x1f${b.prodIncomplete ? 1 : 0}\x1f${b.stagingIncomplete ? 1 : 0}`).join("");
10521
+ }
10522
+ //#endregion
10523
+ //#region ../../../../sdk/model/dist/columns/column_providers/index.js
10524
+ /**
10525
+ * Build the default set of ColumnsProviders for the ambient render ctx:
10526
+ * - `AccessorColumnsProvider` over `outputs` (if present)
10527
+ * - `AccessorColumnsProvider` over `prerun` (if present)
10528
+ * - `ResultPoolColumnsProvider` over `rawResultPool`
10529
+ *
10530
+ * Pulls handles directly from the ambient `cfgRenderCtx`. Returns `[]` when
10531
+ * called outside a render context.
10532
+ *
10533
+ * Result is memoised per-ctx (WeakMap → array). Repeated calls within the
10534
+ * same render cycle return the same provider triplet — inner providers are
10535
+ * also LRU-memoised by their content keys, so even a cache miss here doesn't
10536
+ * re-walk the trees.
10537
+ */
10538
+ const _ctxProvidersCache = /* @__PURE__ */ new WeakMap();
10539
+ function getCtxProviders(deps) {
10540
+ const ctx = deps?.ctx ?? getCfgRenderCtx();
10541
+ const cached = _ctxProvidersCache.get(ctx);
10542
+ if (cached !== void 0) return cached;
10543
+ const providers = [];
10544
+ const outputs = ctx.getAccessorHandleByName(MainAccessorName);
10545
+ if (outputs !== void 0) providers.push(ColumnsProvider(new TreeNodeAccessor(outputs, [MainAccessorName])));
10546
+ const prerun = ctx.getAccessorHandleByName(StagingAccessorName);
10547
+ if (prerun !== void 0) providers.push(ColumnsProvider(new TreeNodeAccessor(prerun, [StagingAccessorName])));
10548
+ providers.push(ColumnsProvider(ctx.getUpstreamBlockCtx()));
10549
+ _ctxProvidersCache.set(ctx, providers);
10550
+ return providers;
10551
+ }
10552
+ //#endregion
10553
+ //#region ../../../../sdk/model/dist/columns/column_lazy.js
10554
+ /**
10555
+ * Thrown by leaf-recipe factories when the requested column is provably
10556
+ * absent in the active render ctx — i.e. every relevant accessor reports
10557
+ * `inputsLocked` and the column did not appear. Distinct from the
10558
+ * `undefined` return that the factories still use for the "resolving" case
10559
+ * (the column may still appear later).
10560
+ *
10561
+ * Catch at the boundary (resolver / filter+sort wiring / data-table assembly)
10562
+ * when partial absence should be surfaced to the user rather than silently
10563
+ * producing an empty result.
10564
+ */
10565
+ var ColumnAbsentError = class extends Error {
10566
+ id;
10567
+ constructor(id) {
10568
+ super(`Column is absent in the active render ctx and will not appear: ${id}`);
10569
+ this.id = id;
10570
+ this.name = "ColumnAbsentError";
10571
+ }
10572
+ };
10573
+ /**
10574
+ * ColumnLazy is the leaf-recipe building block: a {@link ColumnRecipe} whose
10575
+ * `id` is a bare {@link PObjectId} and whose readers are bound to a single
10576
+ * tree-accessor leaf. Layered encodings (Overridden / Discovered / Filtered)
10577
+ * are reified through their dedicated recipe classes and reference leaf
10578
+ * columns by id.
10579
+ */
10580
+ var ColumnLazyImpl = class ColumnLazyImpl {
10581
+ id;
10582
+ options;
10583
+ specCache;
10584
+ dataCache;
10585
+ dataStatusCache;
10586
+ constructor(id, options) {
10587
+ this.id = id;
10588
+ this.options = options;
10589
+ }
10590
+ /** A leaf-recipe references exactly one column — its own id. */
10591
+ getReferencedIds() {
10592
+ return [this.id];
10593
+ }
10594
+ getSpec() {
10595
+ if (this.specCache === void 0) this.specCache = { value: this.options.getSpec() };
10596
+ return this.specCache.value;
10597
+ }
10598
+ /** Leaf-shaped — points straight at `this.id`. */
10599
+ getQuery() {
10600
+ return {
10601
+ type: "column",
10602
+ column: this.id
10603
+ };
10604
+ }
10605
+ /** Leaf-only: not on the recipe interface — only the leaf can read data directly. */
10606
+ getData() {
10607
+ if (this.dataCache === void 0) this.dataCache = { value: this.options.getData() };
10608
+ return this.dataCache.value;
10609
+ }
10610
+ getDataStatus() {
10611
+ if (this.dataStatusCache === void 0) this.dataStatusCache = { value: this.options.getDataStatus() };
10612
+ return this.dataStatusCache.value;
10613
+ }
10614
+ /**
10615
+ * Overlay overrides → produces a {@link ColumnOverriddenRecipe} wrapping
10616
+ * this leaf. ColumnLazy itself stays bare (id remains a plain PObjectId);
10617
+ * layering lives entirely in the recipe wrappers.
10618
+ */
10619
+ withSpecs(overrides) {
10620
+ return ColumnOverriddenRecipe.wrap(this, overrides);
10621
+ }
10622
+ /**
10623
+ * Resolve via the ambient {@link ColumnRegistry}. Spec is resolved eagerly:
10624
+ * returns `undefined` if the leaf isn't reachable yet (resolving). Throws
10625
+ * {@link ColumnAbsentError} when every relevant accessor is `inputsLocked`
10626
+ * and the column did not appear — the column will not exist in this ctx.
10627
+ * Data and dataStatus stay lazy.
10628
+ */
10629
+ static fromId(id, { ctx } = {}) {
10630
+ const registry = new ColumnRegistry(getCtxProviders({ ctx }));
10631
+ const leaf = registry.resolve(id);
10632
+ if (isNil(leaf)) {
10633
+ if (registry.isFinal()) throw new ColumnAbsentError(id);
10634
+ return;
10635
+ }
10636
+ const spec = readSpecAccessor(leaf);
10637
+ if (isNil(spec)) {
10638
+ if (leaf.accessor.getInputsLocked()) throw new ColumnAbsentError(id);
10639
+ return;
10640
+ }
10641
+ if (!spec.hasData()) return void 0;
10642
+ return new ColumnLazyImpl(id, {
10643
+ getSpec: () => spec.getDataAsJson(),
10644
+ getData: () => readDataAccessor(leaf),
10645
+ getDataStatus: () => readDataStatus(leaf)
10646
+ });
10647
+ }
10648
+ /** {@link PlRef} wrapper over {@link fromId}. */
10649
+ static fromPlRef(ref) {
10650
+ return ColumnLazyImpl.fromId(createGlobalPObjectId(ref.blockId, ref.name));
10651
+ }
10652
+ /**
10653
+ * Bind directly to an accessor-backed {@link LeafEntry} — no registry.
10654
+ * Throws {@link ColumnAbsentError} if the leaf has no spec field and its
10655
+ * accessor is `inputsLocked`. Returns `undefined` while still resolving.
10656
+ */
10657
+ static fromAccessor(entry) {
10658
+ const spec = readSpecAccessor(entry);
10659
+ if (isNil(spec)) {
10660
+ if (entry.accessor.getInputsLocked()) throw new ColumnAbsentError(entry.id);
10661
+ return;
10662
+ }
10663
+ if (!spec.hasData()) return void 0;
10664
+ return new ColumnLazyImpl(entry.id, {
10665
+ getSpec: () => spec.getDataAsJson(),
10666
+ getData: () => readDataAccessor(entry),
10667
+ getDataStatus: () => readDataStatus(entry)
10668
+ });
10669
+ }
10670
+ /**
10671
+ * Wrap a materialised {@link PColumn}. If the input is already a
10672
+ * {@link ColumnLazy} it is returned as-is.
10673
+ */
10674
+ static fromColumn(column) {
10675
+ if (column instanceof ColumnLazyImpl) return column;
10676
+ return new ColumnLazyImpl(column.id, {
10677
+ getSpec: () => column.spec,
10678
+ getData: () => column.data,
10679
+ getDataStatus: () => "present"
10680
+ });
10681
+ }
10682
+ /**
10683
+ * Distinguishes `present` / `resolving` / `absent` for a {@link PObjectId}
10684
+ * in the active render ctx. Falls back to the registry's `isFinal()`
10685
+ * when the id has no entry — only then we can say `absent` instead of
10686
+ * `resolving`.
10687
+ */
10688
+ static getStatusById(id, { ctx } = {}) {
10689
+ const registry = new ColumnRegistry(getCtxProviders({ ctx }));
10690
+ const leaf = registry.resolve(id);
10691
+ if (isNil(leaf)) return registry.isFinal() ? "absent" : "resolving";
10692
+ return getLeafEntryStatus(leaf);
10693
+ }
10694
+ /** {@link PlRef} wrapper over {@link getStatusById}. */
10695
+ static getStatusByPlRef(ref, opts = {}) {
10696
+ return ColumnLazyImpl.getStatusById(createGlobalPObjectId(ref.blockId, ref.name), opts);
10697
+ }
10698
+ /** No registry — reads straight off the entry's accessor. */
10699
+ static getStatusByAccessor(entry) {
10700
+ return getLeafEntryStatus(entry);
10701
+ }
10702
+ };
10703
+ /**
10704
+ * Unified dispatcher — picks the right `ColumnLazyImpl.fromX` by source
10705
+ * shape. For ambiguous inputs callers can still use the explicit factories
10706
+ * (also attached as properties: `ColumnLazy.fromId`, `.fromPlRef`,
10707
+ * `.fromAccessor`, `.fromColumn`).
10708
+ */
10709
+ function ColumnLazyDispatch(source, opts = {}) {
10710
+ if (typeof source === "string") return ColumnLazyImpl.fromId(source, opts);
10711
+ if (source instanceof ColumnLazyImpl) return source;
10712
+ if ("accessor" in source) return ColumnLazyImpl.fromAccessor(source);
10713
+ if (isPlRef(source)) return ColumnLazyImpl.fromPlRef(source);
10714
+ if (isPColumn(source)) return ColumnLazyImpl.fromColumn(source);
10715
+ throw new Error("ColumnLazy: unknown source shape");
10716
+ }
10717
+ /**
10718
+ * Polymorphic counterpart to {@link ColumnLazyDispatch}: returns the
10719
+ * {@link ColumnResolutionStatus} for any factory input without constructing
10720
+ * the recipe. For already-materialised sources ({@link PColumn} value,
10721
+ * existing {@link ColumnLazy}) status is `present` by construction.
10722
+ */
10723
+ function ColumnLazyGetStatus(source, opts = {}) {
10724
+ if (typeof source === "string") return ColumnLazyImpl.getStatusById(source, opts);
10725
+ if (source instanceof ColumnLazyImpl) return "present";
10726
+ if ("accessor" in source) return ColumnLazyImpl.getStatusByAccessor(source);
10727
+ if (isPlRef(source)) return ColumnLazyImpl.getStatusByPlRef(source, opts);
10728
+ if (isPColumn(source)) return "present";
10729
+ throw new Error("ColumnLazy.getStatus: unknown source shape");
10730
+ }
10731
+ const ColumnLazy = Object.assign(ColumnLazyDispatch, {
10732
+ fromId: ColumnLazyImpl.fromId,
10733
+ fromPlRef: ColumnLazyImpl.fromPlRef,
10734
+ fromAccessor: ColumnLazyImpl.fromAccessor,
10735
+ fromColumn: ColumnLazyImpl.fromColumn,
10736
+ getStatus: ColumnLazyGetStatus,
10737
+ getStatusById: ColumnLazyImpl.getStatusById,
10738
+ getStatusByPlRef: ColumnLazyImpl.getStatusByPlRef,
10739
+ getStatusByAccessor: ColumnLazyImpl.getStatusByAccessor
10740
+ });
10741
+ const readSpecAccessor = memoizeByEntry(({ accessor, name }) => accessor.traverse({
10742
+ field: `${name}.spec`,
10743
+ assertFieldType: "Input",
10744
+ ignoreError: true
10745
+ }));
10746
+ /**
10747
+ * Per-entry counterpart to {@link readDataStatus}: tells whether the leaf's
10748
+ * **spec** can be read in this ctx, and — for the negative cases —
10749
+ * distinguishes `resolving` from `absent` via `getInputsLocked()`.
10750
+ *
10751
+ * - spec field not yet on the entry's accessor + inputs locked → `absent`
10752
+ * - spec field not yet on the entry's accessor + still resolving → `resolving`
10753
+ * - spec resource present but bytes not yet written → `resolving`
10754
+ * (transient — the spec resource is connected, just unfilled)
10755
+ * - spec resource present and `hasData()` → `present`
10756
+ */
10757
+ function getLeafEntryStatus(entry) {
10758
+ const spec = readSpecAccessor(entry);
10759
+ if (isNil(spec)) return entry.accessor.getInputsLocked() ? "absent" : "resolving";
10760
+ if (!spec.hasData()) return "resolving";
10761
+ return "present";
10762
+ }
10763
+ const readDataAccessor = memoizeByEntry(({ accessor, name }) => accessor.traverse({
10764
+ field: `${name}.data`,
10765
+ assertFieldType: "Input",
10766
+ ignoreError: true
10767
+ }));
10768
+ const readDataStatus = memoizeByEntry(({ accessor, name }) => {
10769
+ if (accessor.listInputFields().includes(`${name}.data`)) return "present";
10770
+ return accessor.getInputsLocked() ? "absent" : "resolving";
10771
+ });
10772
+ function memoizeByEntry(fn) {
10773
+ const cache = new LRUCache({ max: 1e3 });
10774
+ return (entry) => {
10775
+ const key = `${entry.accessor.handle}:${entry.name}`;
10776
+ let hit = cache.get(key);
10777
+ if (!hit) cache.set(key, hit = { value: fn(entry) });
10778
+ return hit.value;
10779
+ };
10780
+ }
10781
+ //#endregion
10782
+ //#region ../../../../sdk/model/dist/columns/column_recipes/index.js
10783
+ /**
10784
+ * Build a `ColumnRecipe` from a stringified id. Dispatches to the matching
10785
+ * concrete recipe variant based on the id's encoding and recurses on
10786
+ * wrappers' inner `source`:
10787
+ *
10788
+ * - bare {@link PObjectId} (non-JSON) → `ColumnLazy.fromId`
10789
+ * - `ColumnDiscoveredKey` → {@link ColumnDiscoveredRecipe}
10790
+ * - `ColumnOverriddenKey { source, specOverrides }` → recurse on `source`,
10791
+ * then {@link ColumnOverriddenRecipe.wrap}
10792
+ * - `ColumnFilteredKey { source, axisFilters }` → recurse on `source`,
10793
+ * then {@link ColumnFilteredRecipe.wrap}
10794
+ *
10795
+ * Returns `undefined` for unsupported variants (e.g. AnchoredPColumnId,
10796
+ * which needs an anchor map to resolve) or when a nested source itself
10797
+ * cannot be built.
10798
+ */
10799
+ function ColumnRecipeBuild(id, opts = {}) {
10800
+ const parsed = parseColumnIdSafely(id);
10801
+ if (isPObjectKey(parsed)) return ColumnLazyImpl.fromId(id, opts);
10802
+ if (isColumnDiscoveredKey(parsed)) return ColumnDiscoveredRecipe.fromKey(parsed, opts);
10803
+ if (isColumnOverriddenKey(parsed)) {
10804
+ const inner = ColumnRecipe(parsed.source, opts);
10805
+ if (inner === void 0) return void 0;
10806
+ return ColumnOverriddenRecipe.wrap(inner, parsed.specOverrides);
10807
+ }
10808
+ if (isColumnFilteredKey(parsed)) {
10809
+ if (typeof parsed.source !== "string") throw new Error(`Unsupported ColumnRecipe id variant: filtered column with non-string source (${JSON.stringify(parsed.source)})`);
10810
+ const inner = ColumnRecipe(parsed.source, opts);
10811
+ if (inner === void 0) return void 0;
10812
+ return ColumnFilteredRecipe.wrap(inner, parsed.axisFilters);
10813
+ }
10814
+ throw new Error(`Unsupported ColumnRecipe id variant: ${id}`);
10815
+ }
10816
+ /**
10817
+ * Resolution status counterpart to {@link ColumnRecipeBuild}. Dispatches on
10818
+ * the parsed id shape to the matching `getStatusBy*` static — never
10819
+ * constructs the recipe. Use this at boundaries (resolver, filter/sort
10820
+ * targets) to throw early on `absent` instead of silently producing an empty
10821
+ * recipe via {@link ColumnRecipeBuild}.
10822
+ */
10823
+ function ColumnRecipeGetStatus(id, opts = {}) {
10824
+ const parsed = parseColumnIdSafely(id);
10825
+ if (isPObjectKey(parsed)) return ColumnLazyImpl.getStatusById(id, opts);
10826
+ if (isColumnDiscoveredKey(parsed)) return ColumnDiscoveredRecipe.getStatusByKey(parsed, opts);
10827
+ if (isColumnOverriddenKey(parsed)) return ColumnOverriddenRecipe.getStatusByKey(parsed, opts);
10828
+ if (isColumnFilteredKey(parsed)) return ColumnFilteredRecipe.getStatusByKey(parsed, opts);
10829
+ throw new Error(`Unsupported ColumnRecipe id variant: ${id}`);
10830
+ }
10831
+ const ColumnRecipe = Object.assign(ColumnRecipeBuild, { getStatus: ColumnRecipeGetStatus });
10832
+ //#endregion
10833
+ //#region ../../../../sdk/model/dist/columns/column.js
10834
+ /**
10835
+ * Unified entry point — routes between the two top-level dispatchers:
10836
+ * - string id (`PObjectId` / `ColumnUniversalId`) → {@link ColumnRecipe}
10837
+ * - object source (`PlRef` / `LeafEntry` / `PColumn` / `ColumnLazy`) → {@link ColumnLazy}
10838
+ *
10839
+ * `ColumnLazy` is itself a `ColumnRecipe<PObjectId>`, so the return type is
10840
+ * the common `ColumnRecipe`.
10841
+ */
10842
+ function Column(source, opts) {
10843
+ if (typeof source === "string") return ColumnRecipe(source, opts);
10844
+ return ColumnLazy(source, opts);
10845
+ }
10846
+ //#endregion
10847
+ //#region ../../../../sdk/model/dist/columns/utils.js
10848
+ /**
10849
+ * Hit-side axis qualifications for the recipe — the ones that should land on
10850
+ * the outer-join entry wrapping this column at the consumer boundary.
10851
+ *
10852
+ * Descends {@link ColumnOverriddenRecipe} / {@link ColumnFilteredRecipe} via
10853
+ * `getInner()` until it finds a {@link ColumnDiscoveredRecipe}; otherwise
10854
+ * returns `[]`. Pure recipe walk — no query-tree introspection.
10855
+ */
10856
+ function hitQualifications(recipe) {
10857
+ return findDiscovered(recipe)?.getColumnQualifications() ?? [];
10858
+ }
10859
+ /**
10860
+ * Per-primary-column axis qualifications for the recipe — applied to the
10861
+ * outer primary anchors on this recipe's group side. Keyed by the external
10862
+ * primary column id (NOT by columns inside this recipe's own query).
10863
+ *
10864
+ * Same walk strategy as {@link hitQualifications}.
10865
+ */
10866
+ function queriesQualifications(recipe) {
10867
+ return findDiscovered(recipe)?.getQueriesQualifications() ?? {};
10868
+ }
10869
+ /**
10870
+ * Walk wrapper layers (Overridden, Filtered) until a {@link
10871
+ * ColumnDiscoveredRecipe} is found. Returns `undefined` if the recipe chain
10872
+ * has no Discovered layer (bare leaves and Overridden-over-leaf cases).
10873
+ *
10874
+ * Invariant from the wrapper classes themselves: there is at most one
10875
+ * Discovered layer in any recipe chain — Discovered is constructed only via
10876
+ * its own factory and is never re-wrapped by Discovered.
10877
+ */
10878
+ function findDiscovered(recipe) {
10879
+ let current = recipe;
10880
+ while (true) {
10881
+ if (current instanceof ColumnDiscoveredRecipe) return current;
10882
+ if (current instanceof ColumnOverriddenRecipe || current instanceof ColumnFilteredRecipe) {
10883
+ current = current.getInner();
10884
+ continue;
10885
+ }
10886
+ return;
10887
+ }
10888
+ }
10889
+ //#endregion
10890
+ //#region ../../../../sdk/model/dist/filters/traverse.js
10891
+ /**
10892
+ * Recursively traverses a FilterSpec tree bottom-up, applying visitor callbacks.
10893
+ *
10894
+ * Entries with `{ type: undefined }` inside `and`/`or` arrays are skipped
10895
+ * (these represent unfilled filter slots in the UI).
10896
+ *
10897
+ * Traversal order:
10898
+ * 1. Recurse into child filters (`and`/`or`/`not`)
10899
+ * 2. Apply the corresponding visitor callback with already-traversed children
10900
+ * 3. For leaf nodes, call `leaf` directly
10901
+ */
10902
+ function traverseFilterSpec(filter, visitor) {
10903
+ return traverseFilterSpecImpl(filter, visitor);
10904
+ }
10905
+ /** Internal implementation with simple generics for clean recursion. */
10906
+ function traverseFilterSpecImpl(filter, visitor) {
10907
+ switch (filter.type) {
10908
+ case "and": return visitor.and(filter.filters.filter((f) => f.type !== void 0).map((f) => traverseFilterSpecImpl(f, visitor)));
10909
+ case "or": return visitor.or(filter.filters.filter((f) => f.type !== void 0).map((f) => traverseFilterSpecImpl(f, visitor)));
10910
+ case "not": return visitor.not(traverseFilterSpecImpl(filter.filter, visitor));
10911
+ default: return visitor.leaf(filter);
10912
+ }
10913
+ }
10914
+ /** Collects all column references (`column` and `rhs` fields) from filter leaves. */
10915
+ function collectFilterSpecColumns(filter) {
10916
+ return traverseFilterSpec(filter, {
10917
+ leaf: (leaf) => {
10918
+ const cols = [];
10919
+ if ("column" in leaf && leaf.column !== void 0) cols.push(leaf.column);
10920
+ if ("rhs" in leaf && leaf.rhs !== void 0) cols.push(leaf.rhs);
10921
+ return cols;
10922
+ },
10923
+ and: (results) => results.flat(),
10924
+ or: (results) => results.flat(),
10925
+ not: (result) => result
10926
+ });
10927
+ }
10928
+ //#endregion
10929
+ //#region ../../../../sdk/model/dist/filters/converters/filterToQuery.js
10930
+ /** Converts a QueryColumnId object into a SpecQueryExpression reference. */
10931
+ function resolveColumnRef(col) {
10932
+ return col.type === "axis" ? {
10933
+ type: "axisRef",
10934
+ value: col.id
10935
+ } : {
10936
+ type: "columnRef",
10937
+ value: col.id
10938
+ };
10939
+ }
10940
+ /** Converts a FilterSpec tree into a SpecQueryExpression. */
10941
+ function filterSpecToSpecQueryExpr(filter) {
10942
+ return traverseFilterSpec(filter, {
10943
+ leaf: leafToSpecQueryExpr,
10944
+ and: (inputs) => {
10945
+ if (inputs.length === 0) throw new Error("AND filter requires at least one operand");
10946
+ return {
10947
+ type: "and",
10948
+ input: inputs
10949
+ };
10950
+ },
10951
+ or: (inputs) => {
10952
+ if (inputs.length === 0) throw new Error("OR filter requires at least one operand");
10953
+ return {
10954
+ type: "or",
10955
+ input: inputs
10956
+ };
10957
+ },
10958
+ not: (input) => ({
10959
+ type: "not",
10960
+ input
10961
+ })
8213
10962
  });
8214
10963
  }
8215
10964
  function leafToSpecQueryExpr(filter) {
@@ -8621,12 +11370,13 @@ function upgradePlDataTableStateV2(state) {
8621
11370
  ...entry,
8622
11371
  filtersState: []
8623
11372
  })),
8624
- pTableParams: createDefaultPTableParams()
11373
+ pTableParams: createDefaultPTableParamsV7()
8625
11374
  };
8626
11375
  if (state.version === 3) state = createPlDataTableStateV2();
8627
11376
  if (state.version === 4) state = migrateV4toV6(state);
8628
11377
  if (state.version === 5) state = migrateV5toV6(state);
8629
11378
  if (state.version === 6) state = migrateV6toV7(state);
11379
+ if (state.version === 7) state = migrateV7toV8(state);
8630
11380
  return state;
8631
11381
  }
8632
11382
  /** Migrate v5 to v6: unwrap `{source, labeled}` colIds in gridState. */
@@ -8637,7 +11387,7 @@ function migrateV5toV6(state) {
8637
11387
  ...entry,
8638
11388
  gridState: unwrapV5GridState(entry.gridState)
8639
11389
  })),
8640
- pTableParams: createDefaultPTableParams()
11390
+ pTableParams: createDefaultPTableParamsV7()
8641
11391
  };
8642
11392
  }
8643
11393
  /** Convert v5 wrapped colId JSON to bare PTableColumnSpec JSON, taking `.source`.
@@ -8694,10 +11444,7 @@ function migrateV4toV6(state) {
8694
11444
  const nextId = () => ++idCounter;
8695
11445
  const migratedCache = state.stateCache.map((entry) => {
8696
11446
  const leaves = [];
8697
- for (const f of entry.filtersState) if (f.filter !== null && !f.filter.disabled) {
8698
- const column = canonicalizeJson(f.id);
8699
- leaves.push(migrateTableFilter(column, f.filter.value, nextId));
8700
- }
11447
+ for (const f of entry.filtersState) if (f.filter !== null && !f.filter.disabled) leaves.push(migrateTableFilter(canonicalizeJson(f.id), f.filter.value, nextId));
8701
11448
  const filtersState = leaves.length > 0 ? {
8702
11449
  id: nextId(),
8703
11450
  type: "and",
@@ -8725,10 +11472,11 @@ function migrateV4toV6(state) {
8725
11472
  filters: distillFilterSpec(currentCache.filtersState),
8726
11473
  defaultFilters: null,
8727
11474
  sorting: state.pTableParams.sorting
8728
- } : createDefaultPTableParams()
11475
+ } : createDefaultPTableParamsV7()
8729
11476
  };
8730
11477
  }
8731
- /** Migrate a single per-column PlTableFilter to a tree-based FilterSpec node */
11478
+ /** Migrate a single per-column PlTableFilter to a v7-form tree node (legacy
11479
+ * string column ids). v7 → v8 later converts these to object form. */
8732
11480
  function migrateTableFilter(column, filter, nextId) {
8733
11481
  const id = nextId();
8734
11482
  switch (filter.type) {
@@ -8854,7 +11602,9 @@ function migrateTableFilter(column, filter, nextId) {
8854
11602
  };
8855
11603
  }
8856
11604
  }
8857
- function createDefaultPTableParams() {
11605
+ /** Default pTableParams in the v7 shape (sorting: []). Used by intermediate
11606
+ * migration steps that produce v6/v7 state before the v7 → v8 conversion. */
11607
+ function createDefaultPTableParamsV7() {
8858
11608
  return {
8859
11609
  sourceId: null,
8860
11610
  hiddenColIds: null,
@@ -8863,13 +11613,109 @@ function createDefaultPTableParams() {
8863
11613
  sorting: []
8864
11614
  };
8865
11615
  }
11616
+ function createDefaultPTableParams() {
11617
+ return {
11618
+ sourceId: null,
11619
+ hiddenColIds: null,
11620
+ filters: null,
11621
+ defaultFilters: null,
11622
+ sorting: null
11623
+ };
11624
+ }
8866
11625
  function createPlDataTableStateV2() {
8867
11626
  return {
8868
- version: 7,
11627
+ version: 8,
8869
11628
  stateCache: [],
8870
11629
  pTableParams: createDefaultPTableParams()
8871
11630
  };
8872
11631
  }
11632
+ function migrateV7toV8(state) {
11633
+ const newCache = state.stateCache.map((entry) => ({
11634
+ sourceId: entry.sourceId,
11635
+ gridState: entry.gridState,
11636
+ sheetsState: entry.sheetsState,
11637
+ filtersState: convertFiltersWithMeta(entry.filtersState),
11638
+ defaultFiltersState: convertFiltersWithMeta(entry.defaultFiltersState),
11639
+ searchString: entry.searchString
11640
+ }));
11641
+ const params = state.pTableParams;
11642
+ let pTableParams;
11643
+ if (params.sourceId === null) pTableParams = createDefaultPTableParams();
11644
+ else pTableParams = {
11645
+ sourceId: params.sourceId,
11646
+ hiddenColIds: params.hiddenColIds,
11647
+ sorting: params.sorting.length === 0 ? null : params.sorting,
11648
+ filters: convertFiltersPlain(params.filters),
11649
+ defaultFilters: convertFiltersPlain(params.defaultFilters)
11650
+ };
11651
+ return {
11652
+ version: 8,
11653
+ stateCache: newCache,
11654
+ pTableParams
11655
+ };
11656
+ }
11657
+ function convertFiltersWithMeta(node) {
11658
+ if (node === null) return null;
11659
+ const result = pruneFilterNode(node);
11660
+ if (result === void 0) return null;
11661
+ return result;
11662
+ }
11663
+ function convertFiltersPlain(node) {
11664
+ if (node === null) return null;
11665
+ const result = pruneFilterNode(node);
11666
+ if (result === void 0) return null;
11667
+ return result;
11668
+ }
11669
+ /** Recursively converts and prunes a legacy v7 filter tree. Preserves meta
11670
+ * fields (id/isExpanded/source/isSuppressed) on non-leaf nodes. Returns
11671
+ * `undefined` when the subtree is empty after pruning. */
11672
+ function pruneFilterNode(node) {
11673
+ if (!node || typeof node !== "object") return void 0;
11674
+ const n = node;
11675
+ if (n.type === "and" || n.type === "or") {
11676
+ const kept = (n.filters ?? []).map((f) => pruneFilterNode(f)).filter((f) => f !== void 0);
11677
+ if (kept.length === 0) return void 0;
11678
+ return {
11679
+ ...n,
11680
+ type: n.type,
11681
+ filters: kept
11682
+ };
11683
+ }
11684
+ if (n.type === "not") {
11685
+ const inner = pruneFilterNode(n.filter);
11686
+ if (inner === void 0) return void 0;
11687
+ return {
11688
+ ...n,
11689
+ type: "not",
11690
+ filter: inner
11691
+ };
11692
+ }
11693
+ return convertLegacyLeaf(n);
11694
+ }
11695
+ function convertLegacyLeaf(leaf) {
11696
+ if (leaf.type === void 0) return leaf;
11697
+ const result = { ...leaf };
11698
+ if ("column" in result) {
11699
+ const c = parseLegacyLeafColumn(result.column);
11700
+ if (c === void 0) return void 0;
11701
+ result.column = c;
11702
+ }
11703
+ if ("rhs" in result) {
11704
+ const c = parseLegacyLeafColumn(result.rhs);
11705
+ if (c === void 0) return void 0;
11706
+ result.rhs = c;
11707
+ }
11708
+ return result;
11709
+ }
11710
+ function parseLegacyLeafColumn(s) {
11711
+ if (typeof s !== "string") return void 0;
11712
+ const parsed = parseJsonSafely(s);
11713
+ if (!parsed || typeof parsed !== "object") return void 0;
11714
+ if (!("type" in parsed) || !("id" in parsed)) return void 0;
11715
+ const t = parsed.type;
11716
+ if (t !== "axis" && t !== "column") return void 0;
11717
+ return parsed;
11718
+ }
8873
11719
  //#endregion
8874
11720
  //#region ../../../../sdk/model/dist/components/PlDataTable/createPlDataTable/utils.js
8875
11721
  /** Check if column is hidden by default */
@@ -8932,18 +11778,29 @@ function getMatchingLabelColumns(columns, allLabelColumns) {
8932
11778
  }
8933
11779
  //#endregion
8934
11780
  //#region ../../../../sdk/model/dist/components/PlDataTable/createPlDataTable/createPTableDefV3.js
11781
+ /**
11782
+ * Assemble a ptable def directly from recipes. Each secondary recipe is its own
11783
+ * outer-joined subtree: `getQuery()` encodes its linker chain, and its
11784
+ * hit-/queries-qualifications are derived on the spot via {@link hitQualifications}
11785
+ * / {@link queriesQualifications} — no pre-extracted DTO. Bare leaves yield empty
11786
+ * qualifications, so plain {@link ColumnRecipe} arrays (e.g. label columns) work
11787
+ * unchanged.
11788
+ */
8935
11789
  function createPTableDefV3(params) {
8936
11790
  let query = {
8937
11791
  type: params.primaryJoinType === "inner" ? "innerJoin" : "fullJoin",
8938
- entries: params.primary.map((a) => toLeaf(a.column, []))
11792
+ entries: params.primary.map((c) => columnToJoinEntry(c, []))
8939
11793
  };
8940
11794
  if (params.secondary.length > 0) query = {
8941
11795
  type: "outerJoin",
8942
11796
  primary: {
8943
11797
  entry: query,
8944
- qualifications: params.secondary.flatMap((g) => params.primary.flatMap((p) => g.primaryQualifications?.[p.column.id] ?? []))
11798
+ qualifications: params.secondary.flatMap((s) => {
11799
+ const quals = queriesQualifications(s);
11800
+ return params.primary.flatMap((p) => quals[p.id] ?? []);
11801
+ })
8945
11802
  },
8946
- secondary: params.secondary.flatMap((g) => g.entries.map((e) => toJoinEntry(e)))
11803
+ secondary: params.secondary.map((c) => columnToJoinEntry(c, hitQualifications(c)))
8947
11804
  };
8948
11805
  if (!isNil(params.filters)) {
8949
11806
  const nonEmpty = distillFilterSpec(params.filters);
@@ -8963,7 +11820,7 @@ function createPTableDefV3(params) {
8963
11820
  sortBy: params.sorting.map((s) => ({
8964
11821
  expression: columnIdToExpr(s.column),
8965
11822
  ascending: s.ascending,
8966
- nullsFirst: !s.naAndAbsentAreLeastValues
11823
+ nullsFirst: s.naAndAbsentAreLeastValues
8967
11824
  }))
8968
11825
  };
8969
11826
  return { query };
@@ -8977,28 +11834,10 @@ function columnIdToExpr(col) {
8977
11834
  value: col.id
8978
11835
  };
8979
11836
  }
8980
- function toLeaf(col, qs) {
8981
- return {
8982
- entry: {
8983
- type: "column",
8984
- column: col
8985
- },
8986
- qualifications: qs
8987
- };
8988
- }
8989
- function toJoinEntry(e) {
8990
- const qs = e.qualifications ?? [];
8991
- if (isNil(e.linkers) || e.linkers.length === 0) return toLeaf(e.column, qs);
11837
+ function columnToJoinEntry(col, qualifications) {
8992
11838
  return {
8993
- ...e.linkers.reduceRight((inner, linker) => ({
8994
- entry: {
8995
- type: "linkerJoin",
8996
- linker: { column: linker },
8997
- secondary: [inner]
8998
- },
8999
- qualifications: []
9000
- }), toLeaf(e.column, [])),
9001
- qualifications: qs
11839
+ entry: col.getQuery(),
11840
+ qualifications
9002
11841
  };
9003
11842
  }
9004
11843
  //#endregion
@@ -9013,14 +11852,38 @@ function createPTableDefV2(params) {
9013
11852
  }
9014
11853
  secondaryColumns.push(...params.labelColumns);
9015
11854
  return createPTableDefV3({
9016
- primary: coreColumns.map((column) => ({ column })),
9017
- secondary: secondaryColumns.map((column) => ({ entries: [{ column }] })),
11855
+ primary: coreColumns.map((column) => ColumnLazyImpl.fromColumn(column)),
11856
+ secondary: secondaryColumns.map((column) => ColumnLazyImpl.fromColumn(column)),
9018
11857
  primaryJoinType: params.coreJoinType,
9019
11858
  filters: params.filters,
9020
11859
  sorting: params.sorting
9021
11860
  });
9022
11861
  }
9023
11862
  //#endregion
11863
+ //#region ../../../../sdk/model/dist/components/PlDataTable/columnResolver.js
11864
+ function createColumnResolver(columns, deps) {
11865
+ const axisIds = [];
11866
+ for (const c of columns) for (const ax of c.getSpec().axesSpec) axisIds.push(getAxisId(ax));
11867
+ const byFullId = /* @__PURE__ */ new Map();
11868
+ const byPObjectId = /* @__PURE__ */ new Map();
11869
+ for (const c of columns) {
11870
+ byFullId.set(c.id, c);
11871
+ const pid = extractPObjectId(c.id);
11872
+ const existing = byPObjectId.get(pid);
11873
+ if (existing === void 0) byPObjectId.set(pid, c);
11874
+ else if (existing.id !== c.id) deps?.warn?.(`Ambiguous PObjectId ${pid}: recipe ids ${existing.id} and ${c.id} both match — keeping first.`);
11875
+ }
11876
+ return (ref) => {
11877
+ if (ref.type === "axis") return axisIds.some((a) => matchAxisId(ref.id, a)) ? ref : void 0;
11878
+ const hit = byFullId.get(ref.id) ?? byPObjectId.get(extractPObjectId(ref.id));
11879
+ if (hit === void 0) return void 0;
11880
+ return {
11881
+ type: "column",
11882
+ id: hit.id
11883
+ };
11884
+ };
11885
+ }
11886
+ //#endregion
9024
11887
  //#region ../../../../sdk/model/dist/components/PlDataTable/createPlDataTable/createPlDataTableV2.js
9025
11888
  /**
9026
11889
  * Create p-table spec and handle given ui table state
@@ -9049,31 +11912,19 @@ function createPlDataTableV2(ctx, columns, tableState, options) {
9049
11912
  };
9050
11913
  });
9051
11914
  const fullColumns = [...columns, ...fullLabelColumns];
9052
- const fullColumnsIds = [...uniqueBy(fullColumns.flatMap((c) => c.spec.axesSpec.map((a) => getAxisId(a))), (a) => canonicalizeJson(a)).map((a) => ({
9053
- type: "axis",
9054
- id: a
9055
- })), ...fullColumns.map((c) => ({
9056
- type: "column",
9057
- id: c.id
9058
- }))];
9059
- const fullColumnsIdsSet = new Set(fullColumnsIds.map((c) => canonicalizeJson(c)));
9060
- const isValidColumnId = (id) => fullColumnsIdsSet.has(id);
9061
- const filters = tableStateNormalized.pTableParams.filters;
9062
- const defaultFilters = options?.filters ?? void 0;
9063
- const firstInvalidFilterColumn = (filters !== null ? collectFilterSpecColumns(filters) : []).find((col) => !isValidColumnId(col));
9064
- if (firstInvalidFilterColumn) throw new Error(`Invalid filter column ${firstInvalidFilterColumn}: column reference does not match the table columns`);
9065
- const firstInvalidDefaultFilterColumn = (defaultFilters !== void 0 ? collectFilterSpecColumns(defaultFilters) : []).find((col) => !isValidColumnId(col));
9066
- if (firstInvalidDefaultFilterColumn) throw new Error(`Invalid default filter column ${firstInvalidDefaultFilterColumn}: column reference does not match the table columns`);
11915
+ const resolver = createColumnResolver(fullColumns.map((c) => ColumnLazyImpl.fromColumn(c)), { warn: ctx.logWarn.bind(ctx) });
11916
+ const rawFilters = tableStateNormalized.pTableParams.filters;
11917
+ const rawDefaultFilters = options?.filters ?? void 0;
11918
+ const filters = remapFiltersThrowing(rawFilters, resolver, "filter");
11919
+ const defaultFilters = remapFiltersThrowing(rawDefaultFilters ?? null, resolver, "default filter");
9067
11920
  const userSorting = tableStateNormalized.pTableParams.sorting;
9068
- const sorting = (isEmpty(userSorting) ? options?.sorting : userSorting) ?? [];
9069
- const firstInvalidSortingColumn = sorting.find((s) => !isValidColumnId(canonicalizeJson(s.column)));
9070
- if (firstInvalidSortingColumn) throw new Error(`Invalid sorting column ${JSON.stringify(firstInvalidSortingColumn.column)}: column reference does not match the table columns`);
11921
+ const sorting = remapSortingThrowing((isNil$1(userSorting) ? options?.sorting : userSorting) ?? [], resolver);
9071
11922
  const coreJoinType = options?.coreJoinType ?? "full";
9072
11923
  const fullDef = createPTableDefV2({
9073
11924
  columns,
9074
11925
  labelColumns: fullLabelColumns,
9075
11926
  coreJoinType,
9076
- filters,
11927
+ filters: filters ?? null,
9077
11928
  sorting,
9078
11929
  coreColumnPredicate: options?.coreColumnPredicate
9079
11930
  });
@@ -9090,10 +11941,7 @@ function createPlDataTableV2(ctx, columns, tableState, options) {
9090
11941
  const coreColumnPredicate = options?.coreColumnPredicate;
9091
11942
  if (coreColumnPredicate) columns.flatMap((c) => coreColumnPredicate(getColumnIdAndSpec(c)) ? [c.id] : []).forEach((c) => hiddenColumns.delete(c));
9092
11943
  sorting.map((s) => s.column).filter((c) => c.type === "column").forEach((c) => hiddenColumns.delete(c.id));
9093
- if (filters) collectFilterSpecColumns(filters).flatMap((c) => {
9094
- const obj = parseJson(c);
9095
- return obj.type === "column" ? [obj.id] : [];
9096
- }).forEach((c) => hiddenColumns.delete(c));
11944
+ if (filters) collectFilterSpecColumns(filters).flatMap((c) => c.type === "column" ? [c.id] : []).forEach((c) => hiddenColumns.delete(c));
9097
11945
  const visibleColumns = columns.filter((c) => !hiddenColumns.has(c.id));
9098
11946
  const visibleLabelColumns = getMatchingLabelColumns(visibleColumns.map(getColumnIdAndSpec), allLabelColumns);
9099
11947
  if (!allPColumnsReady([...visibleColumns, ...visibleLabelColumns])) return void 0;
@@ -9101,7 +11949,7 @@ function createPlDataTableV2(ctx, columns, tableState, options) {
9101
11949
  columns: visibleColumns,
9102
11950
  labelColumns: visibleLabelColumns,
9103
11951
  coreJoinType,
9104
- filters,
11952
+ filters: filters ?? null,
9105
11953
  sorting,
9106
11954
  coreColumnPredicate
9107
11955
  });
@@ -9112,9 +11960,53 @@ function createPlDataTableV2(ctx, columns, tableState, options) {
9112
11960
  fullTableHandle: fullHandle,
9113
11961
  fullPframeHandle: pframeHandle,
9114
11962
  visibleTableHandle: visibleHandle,
9115
- defaultFilters
11963
+ defaultFilters: defaultFilters ?? void 0
9116
11964
  };
9117
11965
  }
11966
+ /** Rewrite filter column refs through the resolver; throw on any unresolved ref. */
11967
+ function remapFiltersThrowing(filters, resolver, label) {
11968
+ if (isNil$1(filters)) return filters;
11969
+ return traverseFilterSpec(filters, {
11970
+ leaf: (leaf) => {
11971
+ if (leaf.type === void 0) return leaf;
11972
+ const result = { ...leaf };
11973
+ if ("column" in result) {
11974
+ const c = resolver(result.column);
11975
+ if (isNil$1(c)) throw new Error(`Invalid ${label} column ${JSON.stringify(result.column)}: column reference does not match the table columns`);
11976
+ result.column = c;
11977
+ }
11978
+ if ("rhs" in result) {
11979
+ const c = resolver(result.rhs);
11980
+ if (isNil$1(c)) throw new Error(`Invalid ${label} column ${JSON.stringify(result.rhs)}: column reference does not match the table columns`);
11981
+ result.rhs = c;
11982
+ }
11983
+ return result;
11984
+ },
11985
+ and: (results) => ({
11986
+ type: "and",
11987
+ filters: results
11988
+ }),
11989
+ or: (results) => ({
11990
+ type: "or",
11991
+ filters: results
11992
+ }),
11993
+ not: (result) => ({
11994
+ type: "not",
11995
+ filter: result
11996
+ })
11997
+ });
11998
+ }
11999
+ /** Rewrite sorting column refs through the resolver; throw on any unresolved ref. */
12000
+ function remapSortingThrowing(sorting, resolver) {
12001
+ return sorting.map((s) => {
12002
+ const c = resolver(s.column);
12003
+ if (isNil$1(c)) throw new Error(`Invalid sorting column ${JSON.stringify(s.column)}: column reference does not match the table columns`);
12004
+ return {
12005
+ ...s,
12006
+ column: c
12007
+ };
12008
+ });
12009
+ }
9118
12010
  function getAllLabelColumns(resultPool) {
9119
12011
  return new PColumnCollection().addAxisLabelProvider(resultPool).addColumnProvider(resultPool).getColumns({
9120
12012
  name: PColumnName.Label,