@bicharts/chart-host 0.5.22 → 0.5.23

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.
@@ -719,8 +719,7 @@ function It(r, n, e, s) {
719
719
  }
720
720
 
721
721
  // src/payload.ts
722
- function buildRenderPayload(cols, rowObjs, geo, point) {
723
- const colNames = cols.map((c) => c.name);
722
+ function resolvePointChannel(cols, rowObjs, point) {
724
723
  let pLat = null;
725
724
  let pLon = null;
726
725
  let pPrec = null;
@@ -773,6 +772,17 @@ function buildRenderPayload(cols, rowObjs, geo, point) {
773
772
  ...rolesRefused.length ? { rolesRefused } : {}
774
773
  };
775
774
  }
775
+ return { lat: pLat, lon: pLon, prec: pPrec, geoPoint, rolesRefused };
776
+ }
777
+ function buildRenderPayload(cols, rowObjs, geo, point, destination) {
778
+ const colNames = cols.map((c) => c.name);
779
+ const o = resolvePointChannel(cols, rowObjs, point);
780
+ const d = resolvePointChannel(cols, rowObjs, destination);
781
+ const pLat = o.lat, pLon = o.lon, pPrec = o.prec;
782
+ const dLat = d.lat, dLon = d.lon, dPrec = d.prec;
783
+ const geoPoint = o.geoPoint;
784
+ const geoPointDest = d.geoPoint;
785
+ const rolesRefused = [...o.rolesRefused, ...d.rolesRefused.map((s) => `destination: ${s}`)];
776
786
  let geoIso = null;
777
787
  let geoUnmatched;
778
788
  let geoUnmatchedDistinct;
@@ -782,7 +792,7 @@ function buildRenderPayload(cols, rowObjs, geo, point) {
782
792
  geoUnmatched = { count: built.totalRows - built.matchedRows, examples: built.unmatched.slice(0, 8) };
783
793
  geoUnmatchedDistinct = built.unmatched.length;
784
794
  }
785
- const extra = 1 + (geoIso ? 1 : 0) + (pLat ? 3 : 0);
795
+ const extra = 1 + (geoIso ? 1 : 0) + (pLat ? 3 : 0) + (dLat ? 3 : 0);
786
796
  const out = new Array(rowObjs.length);
787
797
  for (let r = 0; r < rowObjs.length; r++) {
788
798
  const row = rowObjs[r];
@@ -803,6 +813,11 @@ function buildRenderPayload(cols, rowObjs, geo, point) {
803
813
  a2[k2++] = pLon[r];
804
814
  a2[k2++] = pPrec ? pPrec[r] : null;
805
815
  }
816
+ if (dLat && dLon) {
817
+ a2[k2++] = dLat[r];
818
+ a2[k2++] = dLon[r];
819
+ a2[k2++] = dPrec ? dPrec[r] : null;
820
+ }
806
821
  out[r] = a2;
807
822
  }
808
823
  const colsOut = [...cols, { name: "__rowIdx__", dataType: "Integer", isMeasure: false, modelDesc: "" }];
@@ -815,12 +830,20 @@ function buildRenderPayload(cols, rowObjs, geo, point) {
815
830
  // never a label, never a colour grouping.
816
831
  { name: "__geoPrecision__", dataType: "String", isMeasure: false, modelDesc: "" }
817
832
  );
833
+ if (dLat) colsOut.push(
834
+ { name: "__geoLatD__", dataType: "Double", isMeasure: false, modelDesc: "" },
835
+ { name: "__geoLonD__", dataType: "Double", isMeasure: false, modelDesc: "" },
836
+ { name: "__geoPrecisionD__", dataType: "String", isMeasure: false, modelDesc: "" }
837
+ );
818
838
  return {
819
839
  columns: colsOut,
820
840
  rows: out,
821
841
  geoUnmatched,
822
842
  geoUnmatchedDistinct,
823
843
  geoPoint,
844
+ // Omitted when there is no second endpoint, so every existing chart's options object
845
+ // is unchanged rather than gaining an undefined key.
846
+ ...geoPointDest ? { geoPointDest } : {},
824
847
  // Survives a refusal that placed nothing — see geoPointRefused. Omitted when empty so
825
848
  // the ordinary case adds no bytes.
826
849
  ...rolesRefused.length ? { geoPointRefused: rolesRefused } : {}
@@ -1192,6 +1215,7 @@ function createChartHost(container, config) {
1192
1215
  }
1193
1216
  if (resolved.geoUnmatched === void 0 && data.geoUnmatched) resolved = { ...resolved, geoUnmatched: data.geoUnmatched };
1194
1217
  if (resolved.geoPoint === void 0 && data.geoPoint) resolved = { ...resolved, geoPoint: data.geoPoint };
1218
+ if (resolved.geoPointDest === void 0 && data.geoPointDest) resolved = { ...resolved, geoPointDest: data.geoPointDest };
1195
1219
  try {
1196
1220
  renderFn(container, { columns: data.columns, rows: data.rows }, resolved);
1197
1221
  } catch (err) {
package/dist/index.mjs CHANGED
@@ -42,7 +42,7 @@ import {
42
42
  requiredD3Plugins,
43
43
  resolveOptions,
44
44
  stripEsmExports
45
- } from "./chunk-65LTZEX6.mjs";
45
+ } from "./chunk-L25G7DDO.mjs";
46
46
  import "./chunk-A2GMXZP7.mjs";
47
47
 
48
48
  // src/trivial.ts
package/dist/react.mjs CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  createChartHost,
5
5
  geoFromCache,
6
6
  loadGeo
7
- } from "./chunk-65LTZEX6.mjs";
7
+ } from "./chunk-L25G7DDO.mjs";
8
8
  import "./chunk-A2GMXZP7.mjs";
9
9
 
10
10
  // src/react.tsx
@@ -54,6 +54,16 @@ export interface RenderOptions {
54
54
  rolesBackfilled?: string[];
55
55
  rolesRefused?: string[];
56
56
  } | null;
57
+ geoPointDest?: {
58
+ precision: GeoPointPrecision | null;
59
+ precisionCounts: Record<GeoPointPrecision, number>;
60
+ coarseExamples: string[];
61
+ unplaced: number;
62
+ unplacedExamples: string[];
63
+ ambiguousRows: number;
64
+ rolesBackfilled?: string[];
65
+ rolesRefused?: string[];
66
+ } | null;
57
67
  maxMapPoints?: number;
58
68
  colorScaleLow?: string;
59
69
  colorScaleHigh?: string;
@@ -10,6 +10,10 @@ export interface ChartHostConfig {
10
10
  examples: string[];
11
11
  };
12
12
  geoPoint?: RenderOptions["geoPoint"];
13
+ /** The DESTINATION endpoint's report on an origin-destination flow map — promoted
14
+ * into options exactly like geoPoint, so a host that hands over the whole payload
15
+ * gets both ends annotated without knowing the field exists. */
16
+ geoPointDest?: RenderOptions["geoPointDest"];
13
17
  /** Provenance the MCP stamps into data.sample.json. `hostContract` is checked
14
18
  * against HOST_CONTRACT_VERSION so a major-version drift is LOUD, not silent. */
15
19
  meta?: {
@@ -10,6 +10,21 @@ export interface GeoPointBinding {
10
10
  country?: string;
11
11
  mapKind?: GeoMapKind;
12
12
  }
13
+ /** What a point binding's resolution ACHIEVED, so the chart can annotate honestly rather
14
+ * than implying a precision it does not have. Named (rather than inlined on RenderPayload)
15
+ * because an origin-destination map carries TWO of them, and the two must be the same
16
+ * contract — see `geoPointDest`. */
17
+ export interface GeoPointReport {
18
+ precision: GeoPointPrecision | null;
19
+ precisionCounts: Record<GeoPointPrecision, number>;
20
+ coarseExamples: string[];
21
+ unplaced: number;
22
+ unplacedExamples: string[];
23
+ ambiguousRows: number;
24
+ ambiguousExamples?: string[];
25
+ rolesBackfilled?: string[];
26
+ rolesRefused?: string[];
27
+ }
13
28
  export interface RenderPayload {
14
29
  columns: any[];
15
30
  rows: any[][];
@@ -18,17 +33,7 @@ export interface RenderPayload {
18
33
  examples: string[];
19
34
  };
20
35
  geoUnmatchedDistinct?: number;
21
- geoPoint?: {
22
- precision: GeoPointPrecision | null;
23
- precisionCounts: Record<GeoPointPrecision, number>;
24
- coarseExamples: string[];
25
- unplaced: number;
26
- unplacedExamples: string[];
27
- ambiguousRows: number;
28
- ambiguousExamples?: string[];
29
- rolesBackfilled?: string[];
30
- rolesRefused?: string[];
31
- };
36
+ geoPoint?: GeoPointReport;
32
37
  /**
33
38
  * WHY THERE IS NO MAP — the refusals that survive when nothing could be placed.
34
39
  *
@@ -43,10 +48,22 @@ export interface RenderPayload {
43
48
  * helps call out exceptions / decisions like this, if possible."
44
49
  */
45
50
  geoPointRefused?: string[];
51
+ /**
52
+ * The SECOND endpoint's resolution report (origin-destination flow map, 2026-08-15).
53
+ * Identical in shape to `geoPoint` because it is produced by the identical function —
54
+ * the two ends of a route must be placed by the same rules or the arc between them is a
55
+ * line drawn to the wrong place.
56
+ *
57
+ * Present ONLY when a destination binding was given, which keeps every other chart's
58
+ * options object byte-identical. A flow map annotates from BOTH: "12 of 40 origins" and
59
+ * "31 of 40 destinations positioned at the largest city in each country" are separate
60
+ * facts, and a single blended count would hide which end of the route is the coarse one.
61
+ */
62
+ geoPointDest?: GeoPointReport;
46
63
  }
47
64
  export declare function buildRenderPayload(cols: Array<{
48
65
  name: string;
49
66
  } & Record<string, any>>, rowObjs: Array<Record<string, any>>, geo?: {
50
67
  column: string;
51
68
  kind: string;
52
- } | null, point?: GeoPointBinding | null): RenderPayload;
69
+ } | null, point?: GeoPointBinding | null, destination?: GeoPointBinding | null): RenderPayload;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bicharts/chart-host",
3
- "version": "0.5.22",
3
+ "version": "0.5.23",
4
4
  "description": "Run a BIC-generated D3 chart in any web host: compiles the generated render() function, applies the shared option defaults, resolves mark clicks (through tooltip overlays), owns the selection affordance, and translates row indices between cross-filtered charts. The same contract the BIC Power BI visual implements, minus Power BI. React bindings at @bicharts/chart-host/react.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",