@matthieumordrel/chart-studio 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/README.md +402 -12
  2. package/dist/core/chart-builder-controls.mjs +141 -0
  3. package/dist/core/dashboard.types.d.mts +220 -0
  4. package/dist/core/data-label-defaults.mjs +74 -0
  5. package/dist/core/data-model.types.d.mts +196 -0
  6. package/dist/core/dataset-builder.types.d.mts +51 -0
  7. package/dist/core/dataset-chart-metadata.d.mts +8 -0
  8. package/dist/core/dataset-chart-metadata.mjs +4 -0
  9. package/dist/core/date-range-presets.mjs +1 -1
  10. package/dist/core/define-dashboard.d.mts +8 -0
  11. package/dist/core/define-dashboard.mjs +156 -0
  12. package/dist/core/define-data-model.d.mts +11 -0
  13. package/dist/core/define-data-model.mjs +327 -0
  14. package/dist/core/define-dataset.d.mts +13 -0
  15. package/dist/core/define-dataset.mjs +111 -0
  16. package/dist/core/index.d.mts +17 -0
  17. package/dist/core/infer-columns.mjs +28 -2
  18. package/dist/core/materialized-view.mjs +580 -0
  19. package/dist/core/materialized-view.types.d.mts +223 -0
  20. package/dist/core/model-chart.mjs +242 -0
  21. package/dist/core/model-chart.types.d.mts +199 -0
  22. package/dist/core/model-inference.mjs +169 -0
  23. package/dist/core/model-inference.types.d.mts +71 -0
  24. package/dist/core/pipeline.mjs +32 -1
  25. package/dist/core/schema-builder.mjs +28 -158
  26. package/dist/core/schema-builder.types.d.mts +2 -49
  27. package/dist/core/types.d.mts +59 -8
  28. package/dist/core/use-chart-options.d.mts +35 -8
  29. package/dist/core/use-chart-resolvers.mjs +13 -3
  30. package/dist/core/use-chart.d.mts +16 -12
  31. package/dist/core/use-chart.mjs +136 -34
  32. package/dist/core/use-dashboard.d.mts +190 -0
  33. package/dist/core/use-dashboard.mjs +551 -0
  34. package/dist/index.d.mts +10 -3
  35. package/dist/index.mjs +5 -2
  36. package/dist/ui/chart-canvas.d.mts +11 -4
  37. package/dist/ui/chart-canvas.mjs +45 -34
  38. package/dist/ui/chart-context.d.mts +2 -0
  39. package/dist/ui/chart-context.mjs +2 -0
  40. package/dist/ui/chart-filters-panel.d.mts +1 -1
  41. package/dist/ui/chart-filters-panel.mjs +163 -37
  42. package/dist/ui/chart-group-by-selector.mjs +4 -4
  43. package/dist/ui/chart-time-bucket-selector.mjs +1 -1
  44. package/dist/ui/chart-toolbar-overflow.mjs +5 -13
  45. package/dist/ui/chart-toolbar.mjs +1 -1
  46. package/package.json +1 -1
  47. package/dist/core/define-chart-schema.d.mts +0 -38
  48. package/dist/core/define-chart-schema.mjs +0 -39
@@ -0,0 +1,199 @@
1
+ import { ChartType, ChartTypeConfig, ColumnHintFor, FiltersConfig, GroupByConfig, MetricConfig, ResolvedFilterColumnIdFromSchema, ResolvedGroupByColumnIdFromSchema, ResolvedMetricColumnIdFromSchema, ResolvedXAxisColumnIdFromSchema, TimeBucket, TimeBucketConfig, XAxisConfig } from "./types.mjs";
2
+ import { MetricBuilder, MetricBuilderConfig, SelectableControlBuilder, SelectableControlBuilderConfig } from "./schema-builder.types.mjs";
3
+ import { DatasetColumns, DatasetRow, DefinedDatasetChartSchema } from "./dataset-builder.types.mjs";
4
+ import { MaterializedViewDefinition } from "./materialized-view.types.mjs";
5
+ import { DefinedDataModel, ModelDatasetId, ModelDatasets, ModelRelationshipDefinition } from "./data-model.types.mjs";
6
+
7
+ //#region src/core/model-chart.types.d.ts
8
+ type Simplify<T> = { [TKey in keyof T]: T[TKey] } & {};
9
+ declare const MODEL_CHART_BUILDER_KIND: unique symbol;
10
+ type IsUnion<T, TWhole = T> = T extends TWhole ? ([TWhole] extends [T] ? false : true) : never;
11
+ type StripIdSuffix<TValue extends string> = TValue extends `${infer TPrefix}Id` ? TPrefix : never;
12
+ type MergeColumns<TLeft extends Record<string, unknown> | undefined, TRight extends Record<string, unknown>> = [TLeft] extends [Record<string, unknown>] ? Simplify<Extract<TLeft, Record<string, unknown>> & TRight> : TRight;
13
+ type UnionToIntersection<TUnion> = (TUnion extends unknown ? (value: TUnion) => void : never) extends ((value: infer TIntersection) => void) ? TIntersection : never;
14
+ type AliasableLookupRelationshipUnion<TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends string> = { [TRelationshipId in keyof TRelationships]: TRelationships[TRelationshipId] extends ModelRelationshipDefinition<infer TFromDatasetId extends string, TBaseDatasetId, any, infer TToColumn extends string> ? StripIdSuffix<TToColumn> extends infer TAlias extends string ? TToColumn extends `${string}Id` ? {
15
+ id: Extract<TRelationshipId, string>;
16
+ alias: TAlias;
17
+ fromDataset: TFromDatasetId;
18
+ toColumn: TToColumn;
19
+ } : never : never : never }[keyof TRelationships];
20
+ type UniqueLookupRelationshipUnion<TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends string> = AliasableLookupRelationshipUnion<TRelationships, TBaseDatasetId> extends infer TRelationship ? TRelationship extends {
21
+ alias: infer TAlias extends string;
22
+ } ? IsUnion<Extract<AliasableLookupRelationshipUnion<TRelationships, TBaseDatasetId>, {
23
+ alias: TAlias;
24
+ }>['fromDataset']> extends true ? never : TRelationship : never : never;
25
+ type DirectXAxisFieldId<TDatasets extends ModelDatasets, TBaseDatasetId extends ModelDatasetId<TDatasets>> = ResolvedXAxisColumnIdFromSchema<DatasetRow<TDatasets[TBaseDatasetId]>, TDatasets[TBaseDatasetId]>;
26
+ type DirectGroupByFieldId<TDatasets extends ModelDatasets, TBaseDatasetId extends ModelDatasetId<TDatasets>> = ResolvedGroupByColumnIdFromSchema<DatasetRow<TDatasets[TBaseDatasetId]>, TDatasets[TBaseDatasetId]>;
27
+ type DirectFilterFieldId<TDatasets extends ModelDatasets, TBaseDatasetId extends ModelDatasetId<TDatasets>> = ResolvedFilterColumnIdFromSchema<DatasetRow<TDatasets[TBaseDatasetId]>, TDatasets[TBaseDatasetId]>;
28
+ type DirectMetricFieldId<TDatasets extends ModelDatasets, TBaseDatasetId extends ModelDatasetId<TDatasets>> = ResolvedMetricColumnIdFromSchema<DatasetRow<TDatasets[TBaseDatasetId]>, TDatasets[TBaseDatasetId]>;
29
+ type ModelChartXAxisFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>> = DirectXAxisFieldId<TDatasets, TBaseDatasetId> | Extract<UniqueLookupRelationshipUnion<TRelationships, TBaseDatasetId> extends infer TRelationship ? TRelationship extends {
30
+ alias: infer TAlias extends string;
31
+ fromDataset: infer TFromDatasetId extends ModelDatasetId<TDatasets>;
32
+ } ? `${TAlias}.${ResolvedXAxisColumnIdFromSchema<DatasetRow<TDatasets[TFromDatasetId]>, TDatasets[TFromDatasetId]>}` : never : never, string>;
33
+ type ModelChartGroupByFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>> = DirectGroupByFieldId<TDatasets, TBaseDatasetId> | Extract<UniqueLookupRelationshipUnion<TRelationships, TBaseDatasetId> extends infer TRelationship ? TRelationship extends {
34
+ alias: infer TAlias extends string;
35
+ fromDataset: infer TFromDatasetId extends ModelDatasetId<TDatasets>;
36
+ } ? `${TAlias}.${ResolvedGroupByColumnIdFromSchema<DatasetRow<TDatasets[TFromDatasetId]>, TDatasets[TFromDatasetId]>}` : never : never, string>;
37
+ type ModelChartFilterFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>> = DirectFilterFieldId<TDatasets, TBaseDatasetId> | Extract<UniqueLookupRelationshipUnion<TRelationships, TBaseDatasetId> extends infer TRelationship ? TRelationship extends {
38
+ alias: infer TAlias extends string;
39
+ fromDataset: infer TFromDatasetId extends ModelDatasetId<TDatasets>;
40
+ } ? `${TAlias}.${ResolvedFilterColumnIdFromSchema<DatasetRow<TDatasets[TFromDatasetId]>, TDatasets[TFromDatasetId]>}` : never : never, string>;
41
+ type ModelChartMetricFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>> = DirectMetricFieldId<TDatasets, TBaseDatasetId> | Extract<UniqueLookupRelationshipUnion<TRelationships, TBaseDatasetId> extends infer TRelationship ? TRelationship extends {
42
+ alias: infer TAlias extends string;
43
+ fromDataset: infer TFromDatasetId extends ModelDatasetId<TDatasets>;
44
+ } ? `${TAlias}.${ResolvedMetricColumnIdFromSchema<DatasetRow<TDatasets[TFromDatasetId]>, TDatasets[TFromDatasetId]>}` : never : never, string>;
45
+ type QualifiedModelChartFieldId<TBaseDatasetId extends string, TFieldId extends string> = `${TBaseDatasetId}.${TFieldId}`;
46
+ type InferredModelChartXAxisFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>> = Extract<{ [TBaseDatasetId in ModelDatasetId<TDatasets>]: QualifiedModelChartFieldId<TBaseDatasetId, ModelChartXAxisFieldId<TDatasets, TRelationships, TBaseDatasetId>> }[ModelDatasetId<TDatasets>], string>;
47
+ type InferredModelChartGroupByFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>> = Extract<{ [TBaseDatasetId in ModelDatasetId<TDatasets>]: QualifiedModelChartFieldId<TBaseDatasetId, ModelChartGroupByFieldId<TDatasets, TRelationships, TBaseDatasetId>> }[ModelDatasetId<TDatasets>], string>;
48
+ type InferredModelChartFilterFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>> = Extract<{ [TBaseDatasetId in ModelDatasetId<TDatasets>]: QualifiedModelChartFieldId<TBaseDatasetId, ModelChartFilterFieldId<TDatasets, TRelationships, TBaseDatasetId>> }[ModelDatasetId<TDatasets>], string>;
49
+ type InferredModelChartMetricFieldId<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>> = Extract<{ [TBaseDatasetId in ModelDatasetId<TDatasets>]: QualifiedModelChartFieldId<TBaseDatasetId, ModelChartMetricFieldId<TDatasets, TRelationships, TBaseDatasetId>> }[ModelDatasetId<TDatasets>], string>;
50
+ type BaseDatasetIdFromQualifiedFieldId<TFieldId extends string> = TFieldId extends `${infer TBaseDatasetId}.${string}` ? TBaseDatasetId : never;
51
+ type UniqueQualifiedBaseDatasetId<TFieldIds extends string> = [TFieldIds] extends [never] ? undefined : IsUnion<BaseDatasetIdFromQualifiedFieldId<TFieldIds>> extends true ? never : BaseDatasetIdFromQualifiedFieldId<TFieldIds>;
52
+ type BaseDatasetIdFromSelectableBuilderConfig<TConfig> = UniqueQualifiedBaseDatasetId<Extract<SelectableConfigFieldIds<TConfig>, `${string}.${string}`>>;
53
+ type BaseDatasetIdFromMetricBuilderConfig<TConfig> = UniqueQualifiedBaseDatasetId<Extract<MetricConfigFieldIds<TConfig>, `${string}.${string}`>>;
54
+ type MergeInferredBaseDatasetId<TExistingBaseDatasetId extends string | undefined, TNextBaseDatasetId extends string | undefined> = [TExistingBaseDatasetId] extends [undefined] ? TNextBaseDatasetId : [TNextBaseDatasetId] extends [undefined] ? TExistingBaseDatasetId : TExistingBaseDatasetId extends TNextBaseDatasetId ? TExistingBaseDatasetId : never;
55
+ interface InferredModelChartBuilder<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets> | undefined = undefined, TXAxis extends XAxisConfig<any> | undefined = undefined, TGroupBy extends GroupByConfig<any> | undefined = undefined, TFilters extends FiltersConfig<any> | undefined = undefined, TMetric extends MetricConfig<any> | undefined = undefined, TChartType extends ChartTypeConfig | undefined = undefined, TTimeBucket extends TimeBucketConfig | undefined = undefined, TConnectNulls extends boolean | undefined = undefined> {
56
+ xAxis<const TBuilder extends SelectableControlBuilder<InferredModelChartXAxisFieldId<TDatasets, TRelationships>, true>>(defineXAxis: (xAxis: SelectableControlBuilder<InferredModelChartXAxisFieldId<TDatasets, TRelationships>, true>) => TBuilder): InferredModelChartBuilder<TDatasets, TRelationships, MergeInferredBaseDatasetId<TBaseDatasetId, Extract<BaseDatasetIdFromSelectableBuilderConfig<SelectableControlBuilderConfig<TBuilder>>, ModelDatasetId<TDatasets> | undefined>>, SelectableControlBuilderConfig<TBuilder>, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket, TConnectNulls>;
57
+ groupBy<const TBuilder extends SelectableControlBuilder<InferredModelChartGroupByFieldId<TDatasets, TRelationships>, true>>(defineGroupBy: (groupBy: SelectableControlBuilder<InferredModelChartGroupByFieldId<TDatasets, TRelationships>, true>) => TBuilder): InferredModelChartBuilder<TDatasets, TRelationships, MergeInferredBaseDatasetId<TBaseDatasetId, Extract<BaseDatasetIdFromSelectableBuilderConfig<SelectableControlBuilderConfig<TBuilder>>, ModelDatasetId<TDatasets> | undefined>>, TXAxis, SelectableControlBuilderConfig<TBuilder>, TFilters, TMetric, TChartType, TTimeBucket, TConnectNulls>;
58
+ filters<const TBuilder extends SelectableControlBuilder<InferredModelChartFilterFieldId<TDatasets, TRelationships>, false>>(defineFilters: (filters: SelectableControlBuilder<InferredModelChartFilterFieldId<TDatasets, TRelationships>, false>) => TBuilder): InferredModelChartBuilder<TDatasets, TRelationships, MergeInferredBaseDatasetId<TBaseDatasetId, Extract<BaseDatasetIdFromSelectableBuilderConfig<SelectableControlBuilderConfig<TBuilder>>, ModelDatasetId<TDatasets> | undefined>>, TXAxis, TGroupBy, SelectableControlBuilderConfig<TBuilder>, TMetric, TChartType, TTimeBucket, TConnectNulls>;
59
+ metric<const TBuilder extends MetricBuilder<InferredModelChartMetricFieldId<TDatasets, TRelationships>, any, any, any>>(defineMetric: (metric: MetricBuilder<InferredModelChartMetricFieldId<TDatasets, TRelationships>>) => TBuilder): InferredModelChartBuilder<TDatasets, TRelationships, MergeInferredBaseDatasetId<TBaseDatasetId, Extract<BaseDatasetIdFromMetricBuilderConfig<MetricBuilderConfig<TBuilder>>, ModelDatasetId<TDatasets> | undefined>>, TXAxis, TGroupBy, TFilters, MetricBuilderConfig<TBuilder>, TChartType, TTimeBucket, TConnectNulls>;
60
+ chartType<const TBuilder extends SelectableControlBuilder<ChartType, true>>(defineChartType: (chartType: SelectableControlBuilder<ChartType, true>) => TBuilder): InferredModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, SelectableControlBuilderConfig<TBuilder>, TTimeBucket, TConnectNulls>;
61
+ timeBucket<const TBuilder extends SelectableControlBuilder<TimeBucket, true>>(defineTimeBucket: (timeBucket: SelectableControlBuilder<TimeBucket, true>) => TBuilder): InferredModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, TChartType, SelectableControlBuilderConfig<TBuilder>, TConnectNulls>;
62
+ connectNulls<const TValue extends boolean>(value: TValue): InferredModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket, TValue>;
63
+ readonly [MODEL_CHART_BUILDER_KIND]: 'inferred';
64
+ }
65
+ interface ModelChartStartBuilder<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>> extends InferredModelChartBuilder<TDatasets, TRelationships> {
66
+ from<const TBaseDatasetId extends ModelDatasetId<TDatasets>>(dataset: TBaseDatasetId): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId>;
67
+ }
68
+ interface ModelChartBuilder<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>, TXAxis extends XAxisConfig<any> | undefined = undefined, TGroupBy extends GroupByConfig<any> | undefined = undefined, TFilters extends FiltersConfig<any> | undefined = undefined, TMetric extends MetricConfig<any> | undefined = undefined, TChartType extends ChartTypeConfig | undefined = undefined, TTimeBucket extends TimeBucketConfig | undefined = undefined, TConnectNulls extends boolean | undefined = undefined> {
69
+ xAxis<const TBuilder extends SelectableControlBuilder<ModelChartXAxisFieldId<TDatasets, TRelationships, TBaseDatasetId>, true>>(defineXAxis: (xAxis: SelectableControlBuilder<ModelChartXAxisFieldId<TDatasets, TRelationships, TBaseDatasetId>, true>) => TBuilder): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, SelectableControlBuilderConfig<TBuilder>, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket, TConnectNulls>;
70
+ groupBy<const TBuilder extends SelectableControlBuilder<ModelChartGroupByFieldId<TDatasets, TRelationships, TBaseDatasetId>, true>>(defineGroupBy: (groupBy: SelectableControlBuilder<ModelChartGroupByFieldId<TDatasets, TRelationships, TBaseDatasetId>, true>) => TBuilder): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, SelectableControlBuilderConfig<TBuilder>, TFilters, TMetric, TChartType, TTimeBucket, TConnectNulls>;
71
+ filters<const TBuilder extends SelectableControlBuilder<ModelChartFilterFieldId<TDatasets, TRelationships, TBaseDatasetId>, false>>(defineFilters: (filters: SelectableControlBuilder<ModelChartFilterFieldId<TDatasets, TRelationships, TBaseDatasetId>, false>) => TBuilder): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, SelectableControlBuilderConfig<TBuilder>, TMetric, TChartType, TTimeBucket, TConnectNulls>;
72
+ metric<const TBuilder extends MetricBuilder<ModelChartMetricFieldId<TDatasets, TRelationships, TBaseDatasetId>, any, any, any>>(defineMetric: (metric: MetricBuilder<ModelChartMetricFieldId<TDatasets, TRelationships, TBaseDatasetId>>) => TBuilder): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, MetricBuilderConfig<TBuilder>, TChartType, TTimeBucket, TConnectNulls>;
73
+ chartType<const TBuilder extends SelectableControlBuilder<ChartType, true>>(defineChartType: (chartType: SelectableControlBuilder<ChartType, true>) => TBuilder): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, SelectableControlBuilderConfig<TBuilder>, TTimeBucket, TConnectNulls>;
74
+ timeBucket<const TBuilder extends SelectableControlBuilder<TimeBucket, true>>(defineTimeBucket: (timeBucket: SelectableControlBuilder<TimeBucket, true>) => TBuilder): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, TChartType, SelectableControlBuilderConfig<TBuilder>, TConnectNulls>;
75
+ connectNulls<const TValue extends boolean>(value: TValue): ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket, TValue>;
76
+ readonly [MODEL_CHART_BUILDER_KIND]: 'explicit';
77
+ }
78
+ type ExplicitBuilderBaseDatasetId<TBuilder> = TBuilder extends ModelChartBuilder<any, any, infer TBaseDatasetId, any, any, any, any, any, any, any> ? TBaseDatasetId : never;
79
+ type InferredBuilderBaseDatasetId<TBuilder> = TBuilder extends InferredModelChartBuilder<any, any, infer TBaseDatasetId, any, any, any, any, any, any, any> ? TBaseDatasetId : never;
80
+ type BuilderXAxisConfig<TBuilder> = TBuilder extends ModelChartBuilder<any, any, any, infer TXAxis, any, any, any, any, any, any> ? TXAxis : TBuilder extends InferredModelChartBuilder<any, any, any, infer TXAxis, any, any, any, any, any, any> ? TXAxis : never;
81
+ type BuilderGroupByConfig<TBuilder> = TBuilder extends ModelChartBuilder<any, any, any, any, infer TGroupBy, any, any, any, any, any> ? TGroupBy : TBuilder extends InferredModelChartBuilder<any, any, any, any, infer TGroupBy, any, any, any, any, any> ? TGroupBy : never;
82
+ type BuilderFiltersConfig<TBuilder> = TBuilder extends ModelChartBuilder<any, any, any, any, any, infer TFilters, any, any, any, any> ? TFilters : TBuilder extends InferredModelChartBuilder<any, any, any, any, any, infer TFilters, any, any, any, any> ? TFilters : never;
83
+ type BuilderMetricConfig<TBuilder> = TBuilder extends ModelChartBuilder<any, any, any, any, any, any, infer TMetric, any, any, any> ? TMetric : TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, infer TMetric, any, any, any> ? TMetric : never;
84
+ type BuilderChartTypeConfig<TBuilder> = TBuilder extends ModelChartBuilder<any, any, any, any, any, any, any, infer TChartType, any, any> ? TChartType : TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, any, infer TChartType, any, any> ? TChartType : never;
85
+ type BuilderTimeBucketConfig<TBuilder> = TBuilder extends ModelChartBuilder<any, any, any, any, any, any, any, any, infer TTimeBucket, any> ? TTimeBucket : TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, any, any, infer TTimeBucket, any> ? TTimeBucket : never;
86
+ type BuilderConnectNulls<TBuilder> = TBuilder extends ModelChartBuilder<any, any, any, any, any, any, any, any, any, infer TConnectNulls> ? TConnectNulls : TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, any, any, any, infer TConnectNulls> ? TConnectNulls : never;
87
+ type StripBaseDatasetPrefix<TFieldId extends string, TBaseDatasetId extends string> = TFieldId extends `${TBaseDatasetId}.${infer TRest}` ? TRest : TFieldId;
88
+ type CompiledFieldId<TFieldId extends string> = TFieldId extends `${infer TAlias}.${infer TColumnId}` ? `${TAlias}${Capitalize<TColumnId>}` : TFieldId;
89
+ type SelectableConfigFieldIds<TConfig> = Extract<TConfig extends {
90
+ allowed?: readonly (infer TAllowed extends string)[];
91
+ } ? TAllowed : never, string> | Extract<TConfig extends {
92
+ hidden?: readonly (infer THidden extends string)[];
93
+ } ? THidden : never, string> | Extract<TConfig extends {
94
+ default?: infer TDefault extends string;
95
+ } ? TDefault : never, string>;
96
+ type MetricConfigFieldIds<TConfig> = Extract<(TConfig extends {
97
+ allowed?: readonly (infer TAllowed)[];
98
+ } ? TAllowed : never) | (TConfig extends {
99
+ hidden?: readonly (infer THidden)[];
100
+ } ? THidden : never) | (TConfig extends {
101
+ default?: infer TDefault;
102
+ } ? TDefault : never), {
103
+ kind: 'aggregate';
104
+ columnId: string;
105
+ }>['columnId'];
106
+ type ConfiguredLookupFieldPaths<TBuilder> = Extract<SelectableConfigFieldIds<BuilderXAxisConfig<TBuilder>> | SelectableConfigFieldIds<BuilderGroupByConfig<TBuilder>> | SelectableConfigFieldIds<BuilderFiltersConfig<TBuilder>> | MetricConfigFieldIds<BuilderMetricConfig<TBuilder>>, `${string}.${string}`>;
107
+ type LookupRelationshipForPath<TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends string, TPath extends string> = TPath extends `${infer TAlias}.${string}` ? Extract<UniqueLookupRelationshipUnion<TRelationships, TBaseDatasetId>, {
108
+ alias: TAlias;
109
+ }> : never;
110
+ type LookupColumnValue<TDataset, TColumnId extends string> = TColumnId extends keyof DatasetRow<TDataset> ? DatasetRow<TDataset>[TColumnId] | null : TDataset extends {
111
+ columns?: infer TColumns extends Record<string, unknown>;
112
+ } ? TColumnId extends keyof TColumns ? TColumns[TColumnId] extends {
113
+ kind: 'derived';
114
+ accessor: (row: any) => infer TValue;
115
+ } ? TValue | null : unknown : never : never;
116
+ type ProjectedLookupRowField<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>, TPath extends string> = TPath extends `${string}.${infer TColumnId extends string}` ? LookupRelationshipForPath<TRelationships, TBaseDatasetId, TPath> extends {
117
+ fromDataset: infer TFromDatasetId extends ModelDatasetId<TDatasets>;
118
+ } ? { [TCompiledId in CompiledFieldId<TPath>]: LookupColumnValue<TDatasets[TFromDatasetId], TColumnId> } : {} : {};
119
+ type ProjectedLookupColumns<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>, TLookupPaths extends string> = { [TLookupPath in TLookupPaths as CompiledFieldId<TLookupPath>]: TLookupPath extends `${string}.${infer TColumnId extends string}` ? LookupRelationshipForPath<TRelationships, TBaseDatasetId, TLookupPath> extends {
120
+ fromDataset: infer TFromDatasetId extends ModelDatasetId<TDatasets>;
121
+ } ? ColumnHintFor<LookupColumnValue<TDatasets[TFromDatasetId], TColumnId>, any> : never : never };
122
+ type MapStringArray<TValue> = TValue extends readonly (infer TItem extends string)[] ? readonly CompiledFieldId<TItem>[] : never;
123
+ type MapMetricValue<TValue> = TValue extends {
124
+ kind: 'aggregate';
125
+ columnId: infer TColumnId extends string;
126
+ } ? Simplify<Omit<TValue, 'columnId'> & {
127
+ columnId: CompiledFieldId<TColumnId>;
128
+ }> : TValue;
129
+ type MapMetricArray<TValue> = TValue extends readonly (infer TItem)[] ? readonly MapMetricValue<TItem>[] : never;
130
+ type NormalizeSelectableConfig<TConfig, TBaseDatasetId extends string> = [TConfig] extends [undefined] ? undefined : Simplify<(TConfig extends {
131
+ allowed?: readonly (infer TAllowed)[];
132
+ } ? {
133
+ allowed?: readonly StripBaseDatasetPrefix<Extract<TAllowed, string>, TBaseDatasetId>[];
134
+ } : {}) & (TConfig extends {
135
+ hidden?: readonly (infer THidden)[];
136
+ } ? {
137
+ hidden?: readonly StripBaseDatasetPrefix<Extract<THidden, string>, TBaseDatasetId>[];
138
+ } : {}) & (TConfig extends {
139
+ default?: infer TDefault extends string;
140
+ } ? {
141
+ default?: StripBaseDatasetPrefix<TDefault, TBaseDatasetId>;
142
+ } : {})>;
143
+ type NormalizeMetricValue<TValue, TBaseDatasetId extends string> = TValue extends {
144
+ kind: 'aggregate';
145
+ columnId: infer TColumnId extends string;
146
+ } ? Simplify<Omit<TValue, 'columnId'> & {
147
+ columnId: StripBaseDatasetPrefix<TColumnId, TBaseDatasetId>;
148
+ }> : TValue;
149
+ type NormalizeMetricArray<TValue, TBaseDatasetId extends string> = TValue extends readonly (infer TItem)[] ? readonly NormalizeMetricValue<TItem, TBaseDatasetId>[] : never;
150
+ type NormalizeMetricConfig<TConfig, TBaseDatasetId extends string> = [TConfig] extends [undefined] ? undefined : Simplify<(TConfig extends {
151
+ allowed?: readonly unknown[];
152
+ } ? {
153
+ allowed?: NormalizeMetricArray<TConfig['allowed'], TBaseDatasetId>;
154
+ } : {}) & (TConfig extends {
155
+ hidden?: readonly unknown[];
156
+ } ? {
157
+ hidden?: NormalizeMetricArray<TConfig['hidden'], TBaseDatasetId>;
158
+ } : {}) & (TConfig extends {
159
+ default?: infer TDefault;
160
+ } ? {
161
+ default?: NormalizeMetricValue<TDefault, TBaseDatasetId>;
162
+ } : {})>;
163
+ type ResolvedBuilderBaseDatasetId<TBuilder> = [ExplicitBuilderBaseDatasetId<TBuilder>] extends [never] ? InferredBuilderBaseDatasetId<TBuilder> : ExplicitBuilderBaseDatasetId<TBuilder>;
164
+ type NormalizedBuilderXAxisConfig<TBuilder> = TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, any, any, any, any> ? [ResolvedBuilderBaseDatasetId<TBuilder>] extends [infer TBaseDatasetId extends string] ? NormalizeSelectableConfig<BuilderXAxisConfig<TBuilder>, TBaseDatasetId> : BuilderXAxisConfig<TBuilder> : BuilderXAxisConfig<TBuilder>;
165
+ type NormalizedBuilderGroupByConfig<TBuilder> = TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, any, any, any, any> ? [ResolvedBuilderBaseDatasetId<TBuilder>] extends [infer TBaseDatasetId extends string] ? NormalizeSelectableConfig<BuilderGroupByConfig<TBuilder>, TBaseDatasetId> : BuilderGroupByConfig<TBuilder> : BuilderGroupByConfig<TBuilder>;
166
+ type NormalizedBuilderFiltersConfig<TBuilder> = TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, any, any, any, any> ? [ResolvedBuilderBaseDatasetId<TBuilder>] extends [infer TBaseDatasetId extends string] ? NormalizeSelectableConfig<BuilderFiltersConfig<TBuilder>, TBaseDatasetId> : BuilderFiltersConfig<TBuilder> : BuilderFiltersConfig<TBuilder>;
167
+ type NormalizedBuilderMetricConfig<TBuilder> = TBuilder extends InferredModelChartBuilder<any, any, any, any, any, any, any, any, any, any> ? [ResolvedBuilderBaseDatasetId<TBuilder>] extends [infer TBaseDatasetId extends string] ? NormalizeMetricConfig<BuilderMetricConfig<TBuilder>, TBaseDatasetId> : BuilderMetricConfig<TBuilder> : BuilderMetricConfig<TBuilder>;
168
+ type MapSelectableConfig<TConfig> = [TConfig] extends [undefined] ? undefined : Simplify<(TConfig extends {
169
+ allowed?: readonly string[];
170
+ } ? {
171
+ allowed?: MapStringArray<TConfig['allowed']>;
172
+ } : {}) & (TConfig extends {
173
+ hidden?: readonly string[];
174
+ } ? {
175
+ hidden?: MapStringArray<TConfig['hidden']>;
176
+ } : {}) & (TConfig extends {
177
+ default?: infer TDefault extends string;
178
+ } ? {
179
+ default?: CompiledFieldId<TDefault>;
180
+ } : {})>;
181
+ type MapMetricConfig<TConfig> = [TConfig] extends [undefined] ? undefined : Simplify<(TConfig extends {
182
+ allowed?: readonly unknown[];
183
+ } ? {
184
+ allowed?: MapMetricArray<TConfig['allowed']>;
185
+ } : {}) & (TConfig extends {
186
+ hidden?: readonly unknown[];
187
+ } ? {
188
+ hidden?: MapMetricArray<TConfig['hidden']>;
189
+ } : {}) & (TConfig extends {
190
+ default?: infer TDefault;
191
+ } ? {
192
+ default?: MapMetricValue<TDefault>;
193
+ } : {})>;
194
+ type BroadCompiledModelChartDefinition<TChartId extends string> = DefinedDatasetChartSchema<any, Record<string, unknown> | undefined, XAxisConfig<string> | undefined, GroupByConfig<string> | undefined, FiltersConfig<string> | undefined, MetricConfig<string> | undefined, ChartTypeConfig | undefined, TimeBucketConfig | undefined, boolean | undefined, TChartId, unknown>;
195
+ type CompiledModelChartOwner<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBuilder, TBaseDatasetId extends ModelDatasetId<TDatasets>> = [ConfiguredLookupFieldPaths<TBuilder>] extends [never] ? TDatasets[TBaseDatasetId] : MaterializedViewDefinition<any, Record<string, unknown> | undefined, readonly string[] | undefined, DefinedDataModel<TDatasets, TRelationships, any, any>, string, TBaseDatasetId, string>;
196
+ type CompileModelChartDefinition<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBuilder, TChartId extends string> = CompileModelChartDefinitionFromState<TDatasets, TRelationships, Extract<ResolvedBuilderBaseDatasetId<TBuilder>, ModelDatasetId<TDatasets>>, NormalizedBuilderXAxisConfig<TBuilder>, NormalizedBuilderGroupByConfig<TBuilder>, NormalizedBuilderFiltersConfig<TBuilder>, NormalizedBuilderMetricConfig<TBuilder>, BuilderChartTypeConfig<TBuilder>, BuilderTimeBucketConfig<TBuilder>, BuilderConnectNulls<TBuilder>, TChartId, CompiledModelChartOwner<TDatasets, TRelationships, TBuilder, Extract<ResolvedBuilderBaseDatasetId<TBuilder>, ModelDatasetId<TDatasets>>>>;
197
+ type CompileModelChartDefinitionFromState<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TBaseDatasetId extends ModelDatasetId<TDatasets>, TXAxis extends XAxisConfig<any> | undefined, TGroupBy extends GroupByConfig<any> | undefined, TFilters extends FiltersConfig<any> | undefined, TMetric extends MetricConfig<any> | undefined, TChartType extends ChartTypeConfig | undefined, TTimeBucket extends TimeBucketConfig | undefined, TConnectNulls extends boolean | undefined, TChartId extends string, TOwner = unknown> = [ModelDatasetId<TDatasets>] extends [never] ? BroadCompiledModelChartDefinition<TChartId> : TBaseDatasetId extends ModelDatasetId<TDatasets> ? DefinedDatasetChartSchema<Simplify<DatasetRow<TDatasets[TBaseDatasetId]> & UnionToIntersection<ProjectedLookupRowField<TDatasets, TRelationships, TBaseDatasetId, ConfiguredLookupFieldPaths<ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket, TConnectNulls>>>>>, MergeColumns<DatasetColumns<TDatasets[TBaseDatasetId]>, ProjectedLookupColumns<TDatasets, TRelationships, TBaseDatasetId, ConfiguredLookupFieldPaths<ModelChartBuilder<TDatasets, TRelationships, TBaseDatasetId, TXAxis, TGroupBy, TFilters, TMetric, TChartType, TTimeBucket, TConnectNulls>>>>, MapSelectableConfig<TXAxis>, MapSelectableConfig<TGroupBy>, MapSelectableConfig<TFilters>, MapMetricConfig<TMetric>, TChartType, TTimeBucket, TConnectNulls, TChartId, TOwner> : BroadCompiledModelChartDefinition<TChartId>;
198
+ //#endregion
199
+ export { CompileModelChartDefinition, InferredModelChartBuilder, ModelChartBuilder, ModelChartStartBuilder };
@@ -0,0 +1,169 @@
1
+ //#region src/core/model-inference.ts
2
+ const MODEL_RUNTIME_METADATA = Symbol("model-runtime-metadata");
3
+ function singularizeDatasetId(datasetId) {
4
+ if (datasetId.endsWith("ies")) return `${datasetId.slice(0, -3)}y`;
5
+ if (datasetId.endsWith("s")) return datasetId.slice(0, -1);
6
+ return datasetId;
7
+ }
8
+ function stripIdSuffix(value) {
9
+ return value.endsWith("Id") ? value.slice(0, -2) : void 0;
10
+ }
11
+ function resolveRelationshipAlias(relationship) {
12
+ return stripIdSuffix(relationship.to.column);
13
+ }
14
+ function buildRelationshipId(toDatasetId, toColumnId, fromDatasetId, fromKeyId) {
15
+ return `${toDatasetId}.${toColumnId} -> ${fromDatasetId}.${fromKeyId}`;
16
+ }
17
+ function resolveRelationshipSourceKeyId(dataset) {
18
+ if (dataset.key?.length === 1) return dataset.key[0];
19
+ }
20
+ function buildInferredForeignKeyColumnId(sourceDatasetId, sourceKeyId) {
21
+ if (sourceKeyId === "id") return `${singularizeDatasetId(sourceDatasetId)}Id`;
22
+ return sourceKeyId.endsWith("Id") ? sourceKeyId : void 0;
23
+ }
24
+ function inferModelRelationships(datasets, explicitRelationships, excludedFieldPairs) {
25
+ const relationships = { ...explicitRelationships };
26
+ const metadata = { inferredRelationships: /* @__PURE__ */ new Map() };
27
+ const explicitTargetPairs = new Set(Object.values(explicitRelationships).map((relationship) => `${relationship.to.dataset}.${relationship.to.column}`));
28
+ const candidatesByFieldPair = /* @__PURE__ */ new Map();
29
+ Object.entries(datasets).forEach(([targetDatasetId]) => {
30
+ Object.entries(datasets).forEach(([sourceDatasetId, sourceDataset]) => {
31
+ if (sourceDatasetId === targetDatasetId) return;
32
+ const sourceKeyId = resolveRelationshipSourceKeyId(sourceDataset);
33
+ if (!sourceKeyId) return;
34
+ const targetColumnId = buildInferredForeignKeyColumnId(sourceDatasetId, sourceKeyId);
35
+ if (!targetColumnId) return;
36
+ const fieldPairId = `${targetDatasetId}.${targetColumnId}`;
37
+ if (explicitTargetPairs.has(fieldPairId) || excludedFieldPairs.has(fieldPairId)) return;
38
+ const candidate = {
39
+ id: buildRelationshipId(targetDatasetId, targetColumnId, sourceDatasetId, sourceKeyId),
40
+ fromDataset: sourceDatasetId,
41
+ fromKey: sourceKeyId,
42
+ toDataset: targetDatasetId,
43
+ toColumn: targetColumnId
44
+ };
45
+ const existing = candidatesByFieldPair.get(fieldPairId);
46
+ if (existing) {
47
+ existing.push(candidate);
48
+ return;
49
+ }
50
+ candidatesByFieldPair.set(fieldPairId, [candidate]);
51
+ });
52
+ });
53
+ candidatesByFieldPair.forEach((candidates) => {
54
+ if (candidates.length !== 1) return;
55
+ const candidate = candidates[0];
56
+ relationships[candidate.id] = {
57
+ kind: "relationship",
58
+ id: candidate.id,
59
+ from: {
60
+ dataset: candidate.fromDataset,
61
+ key: candidate.fromKey
62
+ },
63
+ to: {
64
+ dataset: candidate.toDataset,
65
+ column: candidate.toColumn
66
+ },
67
+ reverse: {
68
+ dataset: candidate.toDataset,
69
+ column: candidate.toColumn,
70
+ to: {
71
+ dataset: candidate.fromDataset,
72
+ key: candidate.fromKey
73
+ }
74
+ }
75
+ };
76
+ metadata.inferredRelationships.set(candidate.id, candidate);
77
+ });
78
+ return {
79
+ relationships,
80
+ metadata
81
+ };
82
+ }
83
+ function isVisibleDatasetColumn(column) {
84
+ return column !== false;
85
+ }
86
+ function isCategoricalLabelColumn(column) {
87
+ if (!column || column === false || typeof column !== "object") return false;
88
+ const typedColumn = column;
89
+ if (typedColumn["kind"] === "derived") return typedColumn["type"] === "category" || typedColumn["type"] === "boolean";
90
+ if ("type" in typedColumn) return typedColumn["type"] === "category" || typedColumn["type"] === "boolean";
91
+ return true;
92
+ }
93
+ function selectAttributeLabelColumn(_datasetId, dataset, sourceKeyId) {
94
+ for (const preferredId of [
95
+ "name",
96
+ "title",
97
+ "label"
98
+ ]) {
99
+ if (preferredId === sourceKeyId) continue;
100
+ const column = dataset.columns?.[preferredId];
101
+ if (column !== void 0 && isVisibleDatasetColumn(column) && isCategoricalLabelColumn(column)) return preferredId;
102
+ }
103
+ if (dataset.columns) {
104
+ for (const [columnId, column] of Object.entries(dataset.columns)) if (columnId !== sourceKeyId && !columnId.endsWith("Id") && isVisibleDatasetColumn(column) && isCategoricalLabelColumn(column)) return columnId;
105
+ }
106
+ if (dataset.columns?.[sourceKeyId] !== false) return sourceKeyId;
107
+ return dataset.columns ? Object.keys(dataset.columns).find((columnId) => columnId !== sourceKeyId && dataset.columns?.[columnId] !== false) : sourceKeyId;
108
+ }
109
+ function inferModelAttributes(datasets, relationships, explicitAttributes) {
110
+ const attributes = { ...explicitAttributes };
111
+ const relationshipsByAlias = /* @__PURE__ */ new Map();
112
+ Object.values(relationships).forEach((relationship) => {
113
+ const alias = resolveRelationshipAlias(relationship);
114
+ if (!alias) return;
115
+ const existing = relationshipsByAlias.get(alias);
116
+ if (existing) {
117
+ existing.push(relationship);
118
+ return;
119
+ }
120
+ relationshipsByAlias.set(alias, [relationship]);
121
+ });
122
+ relationshipsByAlias.forEach((matchingRelationships, alias) => {
123
+ if (alias in attributes) return;
124
+ const sourceDatasetIds = [...new Set(matchingRelationships.map((relationship) => relationship.from.dataset))];
125
+ const sourceKeyIds = [...new Set(matchingRelationships.map((relationship) => relationship.from.key))];
126
+ if (sourceDatasetIds.length !== 1 || sourceKeyIds.length !== 1) return;
127
+ const sourceDatasetId = sourceDatasetIds[0];
128
+ const sourceKeyId = sourceKeyIds[0];
129
+ const sourceDataset = datasets[sourceDatasetId];
130
+ if (!sourceDataset) return;
131
+ const labelColumnId = selectAttributeLabelColumn(sourceDatasetId, sourceDataset, sourceKeyId);
132
+ if (!labelColumnId) return;
133
+ attributes[alias] = {
134
+ id: alias,
135
+ kind: "select",
136
+ source: {
137
+ dataset: sourceDatasetId,
138
+ key: sourceKeyId,
139
+ label: labelColumnId
140
+ },
141
+ targets: matchingRelationships.map((relationship) => ({
142
+ dataset: relationship.to.dataset,
143
+ column: relationship.to.column,
144
+ via: relationship.id
145
+ }))
146
+ };
147
+ });
148
+ return attributes;
149
+ }
150
+ function rewriteInferredRelationshipError(metadata, error) {
151
+ if (!(error instanceof Error)) throw error;
152
+ for (const relationship of metadata.inferredRelationships.values()) if (error.message.startsWith(`Relationship "${relationship.id}"`)) throw new Error(`Inferred relationship "${relationship.toDataset}.${relationship.toColumn} -> ${relationship.fromDataset}.${relationship.fromKey}" failed validation: ${error.message.slice(`Relationship "${relationship.id}" `.length)} If this is not a real foreign key, exclude it with: exclude: ['${relationship.toDataset}.${relationship.toColumn}']`);
153
+ throw error;
154
+ }
155
+ function attachModelRuntimeMetadata(model, metadata) {
156
+ Object.defineProperty(model, MODEL_RUNTIME_METADATA, {
157
+ value: { inferredRelationships: new Map(metadata.inferredRelationships) },
158
+ enumerable: false,
159
+ configurable: false,
160
+ writable: false
161
+ });
162
+ }
163
+ function getModelRuntimeMetadata(model) {
164
+ const metadata = model[MODEL_RUNTIME_METADATA];
165
+ if (metadata && typeof metadata === "object") return metadata;
166
+ return { inferredRelationships: /* @__PURE__ */ new Map() };
167
+ }
168
+ //#endregion
169
+ export { attachModelRuntimeMetadata, getModelRuntimeMetadata, inferModelAttributes, inferModelRelationships, resolveRelationshipAlias, rewriteInferredRelationshipError };
@@ -0,0 +1,71 @@
1
+ import { InferableFieldKey, ResolvedGroupByColumnIdFromSchema } from "./types.mjs";
2
+ import { DatasetRow, DefinedDataset, SingleDatasetKeyId } from "./dataset-builder.types.mjs";
3
+ import { ModelAttributeDefinition, ModelDatasets, ModelRelationshipDefinition } from "./data-model.types.mjs";
4
+
5
+ //#region src/core/model-inference.types.d.ts
6
+ type Simplify<T> = { [TKey in keyof T]: T[TKey] } & {};
7
+ type IsUnion<T, TWhole = T> = T extends TWhole ? ([TWhole] extends [T] ? false : true) : never;
8
+ type SingularizeDatasetId<TDatasetId extends string> = TDatasetId extends `${infer TStem}ies` ? `${TStem}y` : TDatasetId extends `${infer TStem}s` ? TStem : TDatasetId;
9
+ type StripIdSuffix<TValue extends string> = TValue extends `${infer TPrefix}Id` ? TPrefix : never;
10
+ type ExplicitTargetPairUnion<TRelationships extends Record<string, ModelRelationshipDefinition>> = { [TRelationshipId in keyof TRelationships]: TRelationships[TRelationshipId] extends ModelRelationshipDefinition<any, infer TToDatasetId extends string, any, infer TToColumn extends string> ? `${TToDatasetId}.${TToColumn}` : never }[keyof TRelationships];
11
+ type ExcludedFieldPairUnion<TExclude> = TExclude extends readonly (infer TFieldPair extends string)[] ? TFieldPair : never;
12
+ type SafeRelationshipKeyId<TDataset> = SingleDatasetKeyId<TDataset> extends infer TKeyId extends string ? TKeyId : never;
13
+ type InferredForeignKeyColumnId<TDatasetId extends string, TKeyId extends string> = TKeyId extends 'id' ? `${SingularizeDatasetId<TDatasetId>}Id` : TKeyId extends `${string}Id` ? TKeyId : never;
14
+ type RelationshipCandidate<TFromDatasetId extends string, TFromKey extends string, TToDatasetId extends string, TToColumn extends string> = {
15
+ id: `${TToDatasetId}.${TToColumn} -> ${TFromDatasetId}.${TFromKey}`;
16
+ pair: `${TToDatasetId}.${TToColumn}`;
17
+ alias: StripIdSuffix<TToColumn>;
18
+ fromDataset: TFromDatasetId;
19
+ fromKey: TFromKey;
20
+ toDataset: TToDatasetId;
21
+ toColumn: TToColumn;
22
+ };
23
+ type InferredRelationshipCandidateUnion<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TExclude extends readonly string[] | undefined> = { [TToDatasetId in Extract<keyof TDatasets, string>]: { [TFromDatasetId in Extract<keyof TDatasets, string>]: TFromDatasetId extends TToDatasetId ? never : SafeRelationshipKeyId<TDatasets[TFromDatasetId]> extends infer TFromKey extends string ? InferredForeignKeyColumnId<TFromDatasetId, TFromKey> extends infer TToColumn extends string ? TToColumn extends InferableFieldKey<DatasetRow<TDatasets[TToDatasetId]>> ? `${TToDatasetId}.${TToColumn}` extends ExplicitTargetPairUnion<TRelationships> | ExcludedFieldPairUnion<TExclude> ? never : RelationshipCandidate<TFromDatasetId, TFromKey, TToDatasetId, TToColumn> : never : never : never }[Extract<keyof TDatasets, string>] }[Extract<keyof TDatasets, string>];
24
+ type UniqueRelationshipCandidateUnion<TCandidates> = TCandidates extends infer TCandidate ? TCandidate extends {
25
+ pair: infer TPair extends string;
26
+ } ? Extract<TCandidates, {
27
+ pair: TPair;
28
+ }> extends infer TMatchingCandidate ? TMatchingCandidate extends {
29
+ fromDataset: infer TFromDataset;
30
+ } ? IsUnion<TFromDataset> extends true ? never : TCandidate : never : never : never : never;
31
+ type AliasableRelationshipUnion<TRelationships extends Record<string, ModelRelationshipDefinition>> = { [TRelationshipId in keyof TRelationships]: TRelationships[TRelationshipId] extends ModelRelationshipDefinition<infer TFromDatasetId extends string, infer TToDatasetId extends string, infer TFromKey extends string, infer TToColumn extends string> ? StripIdSuffix<TToColumn> extends infer TAlias extends string ? TToColumn extends `${string}Id` ? {
32
+ id: Extract<TRelationshipId, string>;
33
+ alias: TAlias;
34
+ fromDataset: TFromDatasetId;
35
+ fromKey: TFromKey;
36
+ toDataset: TToDatasetId;
37
+ toColumn: TToColumn;
38
+ } : never : never : never }[keyof TRelationships];
39
+ type LabelColumnCandidates<TDataset extends DefinedDataset<any, any, any>> = Exclude<ResolvedGroupByColumnIdFromSchema<DatasetRow<TDataset>, TDataset>, SafeRelationshipKeyId<TDataset> | `${string}Id`> | SafeRelationshipKeyId<TDataset>;
40
+ type InferredAttributeAliasUnion<TRelationships extends Record<string, ModelRelationshipDefinition>, TAttributes extends Record<string, ModelAttributeDefinition>> = Exclude<AliasableRelationshipUnion<TRelationships>['alias'], Extract<keyof TAttributes, string>>;
41
+ type UniqueAttributeAliasUnion<TRelationships extends Record<string, ModelRelationshipDefinition>, TAttributes extends Record<string, ModelAttributeDefinition>> = InferredAttributeAliasUnion<TRelationships, TAttributes> extends infer TAlias ? TAlias extends string ? IsUnion<Extract<AliasableRelationshipUnion<TRelationships>, {
42
+ alias: TAlias;
43
+ }>['fromDataset']> extends true ? never : TAlias : never : never;
44
+ type AttributeSourceDatasetId<TRelationships extends Record<string, ModelRelationshipDefinition>, TAlias extends string> = Extract<Extract<AliasableRelationshipUnion<TRelationships>, {
45
+ alias: TAlias;
46
+ }>['fromDataset'], string>;
47
+ type AttributeSourceKeyId<TRelationships extends Record<string, ModelRelationshipDefinition>, TAlias extends string> = Extract<Extract<AliasableRelationshipUnion<TRelationships>, {
48
+ alias: TAlias;
49
+ }>['fromKey'], string>;
50
+ type AttributeTargetsForAlias<TRelationships extends Record<string, ModelRelationshipDefinition>, TAlias extends string> = readonly (Extract<AliasableRelationshipUnion<TRelationships>, {
51
+ alias: TAlias;
52
+ }> extends infer TRelationship ? TRelationship extends {
53
+ id: infer TRelationshipId extends string;
54
+ toDataset: infer TToDatasetId extends string;
55
+ toColumn: infer TToColumn extends string;
56
+ } ? {
57
+ dataset: TToDatasetId;
58
+ column: TToColumn;
59
+ via: TRelationshipId;
60
+ } : never : never)[];
61
+ type CanInferAttributeForAlias<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TAlias extends string> = LabelColumnCandidates<TDatasets[AttributeSourceDatasetId<TRelationships, TAlias>]> extends never ? false : true;
62
+ type InferRelationshipsForModel<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TExclude extends readonly string[] | undefined> = Simplify<TRelationships & { [TRelationship in UniqueRelationshipCandidateUnion<InferredRelationshipCandidateUnion<TDatasets, TRelationships, TExclude>> as TRelationship['id']]: ModelRelationshipDefinition<TRelationship['fromDataset'], TRelationship['toDataset'], TRelationship['fromKey'], TRelationship['toColumn']> }>;
63
+ type InferAttributesForModel<TDatasets extends ModelDatasets, TRelationships extends Record<string, ModelRelationshipDefinition>, TAttributes extends Record<string, ModelAttributeDefinition>> = Simplify<TAttributes & { [TAlias in UniqueAttributeAliasUnion<TRelationships, TAttributes> as CanInferAttributeForAlias<TDatasets, TRelationships, TAlias> extends true ? TAlias : never]: ModelAttributeDefinition<AttributeSourceDatasetId<TRelationships, TAlias>, AttributeTargetsForAlias<TRelationships, TAlias>> & {
64
+ source: {
65
+ dataset: AttributeSourceDatasetId<TRelationships, TAlias>;
66
+ key: AttributeSourceKeyId<TRelationships, TAlias>;
67
+ label: LabelColumnCandidates<TDatasets[AttributeSourceDatasetId<TRelationships, TAlias>]>;
68
+ };
69
+ } }>;
70
+ //#endregion
71
+ export { InferAttributesForModel, InferRelationshipsForModel };
@@ -139,6 +139,37 @@ function extractAvailableFilters(data, columns) {
139
139
  });
140
140
  }
141
141
  /**
142
+ * Recompute option counts using cross-filtering: for each column, apply filters
143
+ * from all *other* columns, then count. This keeps same-column options from
144
+ * zeroing each other out while still reflecting the impact of other filters.
145
+ *
146
+ * @param baseFilters - The available filters with labels and original ordering
147
+ * @param data - Data already filtered by date range (effectiveData)
148
+ * @param columns - All column definitions
149
+ * @param activeFilters - The user's current filter selections
150
+ * @returns A new array of AvailableFilter with updated counts
151
+ */
152
+ function computeFilteredCounts(baseFilters, data, columns, activeFilters) {
153
+ if (activeFilters.size === 0) return baseFilters;
154
+ return baseFilters.map((filter) => {
155
+ const crossFiltered = applyFilters(data, columns, new Map([...activeFilters].filter(([columnId]) => columnId !== filter.columnId)));
156
+ const column = columns.find((c) => c.id === filter.columnId);
157
+ if (!column) return filter;
158
+ const countMap = /* @__PURE__ */ new Map();
159
+ for (const item of crossFiltered) {
160
+ const value = getStringValue(item, column);
161
+ countMap.set(value, (countMap.get(value) ?? 0) + 1);
162
+ }
163
+ return {
164
+ ...filter,
165
+ options: filter.options.map((option) => ({
166
+ ...option,
167
+ count: countMap.get(option.value) ?? 0
168
+ }))
169
+ };
170
+ });
171
+ }
172
+ /**
142
173
  * Derive one human-facing filter option label from the typed column value while
143
174
  * keeping the underlying filter state keyed by the stable string value.
144
175
  */
@@ -150,4 +181,4 @@ function formatFilterOptionLabel(item, column) {
150
181
  });
151
182
  }
152
183
  //#endregion
153
- export { applyFilters, extractAvailableFilters, runPipeline };
184
+ export { applyFilters, computeFilteredCounts, extractAvailableFilters, runPipeline };