@bicharts/chart-host 0.5.21 → 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 } : {}
@@ -1020,6 +1043,11 @@ var ESM_EXPORT_DECL = /^([ \t]*)export[ \t]+(?=(?:async[ \t]+)?function\b|const\
1020
1043
  function stripEsmExports(code) {
1021
1044
  return String(code || "").replace(ESM_EXPORT_CLAUSE, "").replace(ESM_EXPORT_DECL, "$1");
1022
1045
  }
1046
+ function writableD3(d3) {
1047
+ if (d3 == null || typeof d3 !== "object") return d3;
1048
+ if (Object.isExtensible(d3)) return d3;
1049
+ return { ...d3 };
1050
+ }
1023
1051
  function compileRenderFn(code, win, doc, d3) {
1024
1052
  const body = stripEsmExports(code) + "\n;return (typeof render === 'function') ? render : null;";
1025
1053
  let factory;
@@ -1032,7 +1060,7 @@ function compileRenderFn(code, win, doc, d3) {
1032
1060
  "[@bicharts/chart-host] the generated code could not be COMPILED, so nothing was drawn. This path evaluates the chart as a code string, which cannot contain ES-module syntax" + (modular ? " \u2014 and this code still has `import`/`export` in it after the export clause was stripped. Either request the plain form from the generator, or `import` the module yourself and pass the render function directly." : ".") + " Original error: " + msg
1033
1061
  );
1034
1062
  }
1035
- const fn = factory.call(null, null, null, null, win, doc, d3);
1063
+ const fn = factory.call(null, null, null, null, win, doc, writableD3(d3));
1036
1064
  if (typeof fn !== "function") throw new Error("generated code did not define render(container, data, options)");
1037
1065
  return fn;
1038
1066
  }
@@ -1187,6 +1215,7 @@ function createChartHost(container, config) {
1187
1215
  }
1188
1216
  if (resolved.geoUnmatched === void 0 && data.geoUnmatched) resolved = { ...resolved, geoUnmatched: data.geoUnmatched };
1189
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 };
1190
1219
  try {
1191
1220
  renderFn(container, { columns: data.columns, rows: data.rows }, resolved);
1192
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-QIF3TAVP.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-QIF3TAVP.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.21",
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",