@toclocoinc/lattice-grid 1.68.0 → 1.68.2

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 (164) hide show
  1. package/README.md +26 -1
  2. package/angular/fesm2022/toclocoinc-lattice-grid-angular.mjs +1 -0
  3. package/angular/package.json +1 -1
  4. package/docs/API.html +8058 -3962
  5. package/docs/api-detail.html +101 -4
  6. package/lattice-grid.d.ts +5917 -78
  7. package/lattice-grid.esm.min.js +197 -39
  8. package/lattice-grid.min.cjs +197 -39
  9. package/lattice-grid.min.js +197 -39
  10. package/modules/ai.d.ts +172 -11
  11. package/modules/ai.esm.min.js +3 -3
  12. package/modules/ai.min.cjs +3 -3
  13. package/modules/ai.min.js +3 -3
  14. package/modules/angular.d.ts +1 -1
  15. package/modules/angular.esm.min.js +3 -3
  16. package/modules/angular.min.cjs +3 -3
  17. package/modules/angular.min.js +3 -3
  18. package/modules/chart-alluvial.d.ts +1 -1
  19. package/modules/chart-alluvial.esm.min.js +1 -1
  20. package/modules/chart-alluvial.min.cjs +1 -1
  21. package/modules/chart-alluvial.min.js +1 -1
  22. package/modules/chart-arc.d.ts +1 -1
  23. package/modules/chart-arc.esm.min.js +1 -1
  24. package/modules/chart-arc.min.cjs +1 -1
  25. package/modules/chart-arc.min.js +1 -1
  26. package/modules/chart-bubblemap.d.ts +1 -1
  27. package/modules/chart-bubblemap.esm.min.js +1 -1
  28. package/modules/chart-bubblemap.min.cjs +1 -1
  29. package/modules/chart-bubblemap.min.js +1 -1
  30. package/modules/chart-bump.d.ts +1 -1
  31. package/modules/chart-bump.esm.min.js +1 -1
  32. package/modules/chart-bump.min.cjs +1 -1
  33. package/modules/chart-bump.min.js +1 -1
  34. package/modules/chart-calendar.d.ts +1 -1
  35. package/modules/chart-calendar.esm.min.js +1 -1
  36. package/modules/chart-calendar.min.cjs +1 -1
  37. package/modules/chart-calendar.min.js +1 -1
  38. package/modules/chart-decomposition.d.ts +1 -1
  39. package/modules/chart-decomposition.esm.min.js +1 -1
  40. package/modules/chart-decomposition.min.cjs +1 -1
  41. package/modules/chart-decomposition.min.js +1 -1
  42. package/modules/chart-diverging.d.ts +1 -1
  43. package/modules/chart-diverging.esm.min.js +1 -1
  44. package/modules/chart-diverging.min.cjs +1 -1
  45. package/modules/chart-diverging.min.js +1 -1
  46. package/modules/chart-dumbbell.d.ts +1 -1
  47. package/modules/chart-dumbbell.esm.min.js +1 -1
  48. package/modules/chart-dumbbell.min.cjs +1 -1
  49. package/modules/chart-dumbbell.min.js +1 -1
  50. package/modules/chart-fan.d.ts +1 -1
  51. package/modules/chart-fan.esm.min.js +1 -1
  52. package/modules/chart-fan.min.cjs +1 -1
  53. package/modules/chart-fan.min.js +1 -1
  54. package/modules/chart-hexbin.d.ts +1 -1
  55. package/modules/chart-hexbin.esm.min.js +1 -1
  56. package/modules/chart-hexbin.min.cjs +1 -1
  57. package/modules/chart-hexbin.min.js +1 -1
  58. package/modules/chart-hexmap.d.ts +1 -1
  59. package/modules/chart-hexmap.esm.min.js +1 -1
  60. package/modules/chart-hexmap.min.cjs +1 -1
  61. package/modules/chart-hexmap.min.js +1 -1
  62. package/modules/chart-icicle.d.ts +1 -1
  63. package/modules/chart-icicle.esm.min.js +1 -1
  64. package/modules/chart-icicle.min.cjs +1 -1
  65. package/modules/chart-icicle.min.js +1 -1
  66. package/modules/chart-markermap.d.ts +1 -1
  67. package/modules/chart-markermap.esm.min.js +1 -1
  68. package/modules/chart-markermap.min.cjs +1 -1
  69. package/modules/chart-markermap.min.js +1 -1
  70. package/modules/chart-parallel.d.ts +1 -1
  71. package/modules/chart-parallel.esm.min.js +1 -1
  72. package/modules/chart-parallel.min.cjs +1 -1
  73. package/modules/chart-parallel.min.js +1 -1
  74. package/modules/chart-ridgeline.d.ts +1 -1
  75. package/modules/chart-ridgeline.esm.min.js +1 -1
  76. package/modules/chart-ridgeline.min.cjs +1 -1
  77. package/modules/chart-ridgeline.min.js +1 -1
  78. package/modules/chart-roc.d.ts +1 -1
  79. package/modules/chart-roc.esm.min.js +1 -1
  80. package/modules/chart-roc.min.cjs +1 -1
  81. package/modules/chart-roc.min.js +1 -1
  82. package/modules/chart-slope.d.ts +1 -1
  83. package/modules/chart-slope.esm.min.js +1 -1
  84. package/modules/chart-slope.min.cjs +1 -1
  85. package/modules/chart-slope.min.js +1 -1
  86. package/modules/chart-splom.d.ts +1 -1
  87. package/modules/chart-splom.esm.min.js +1 -1
  88. package/modules/chart-splom.min.cjs +1 -1
  89. package/modules/chart-splom.min.js +1 -1
  90. package/modules/chart-waffle.d.ts +1 -1
  91. package/modules/chart-waffle.esm.min.js +1 -1
  92. package/modules/chart-waffle.min.cjs +1 -1
  93. package/modules/chart-waffle.min.js +1 -1
  94. package/modules/charts.d.ts +16 -1
  95. package/modules/charts.esm.min.js +23 -16
  96. package/modules/charts.min.cjs +23 -16
  97. package/modules/charts.min.js +23 -16
  98. package/modules/data-router.d.ts +259 -3
  99. package/modules/data-router.esm.min.js +9 -4
  100. package/modules/data-router.min.cjs +9 -4
  101. package/modules/data-router.min.js +9 -4
  102. package/modules/devtools.d.ts +1 -1
  103. package/modules/devtools.esm.min.js +1 -1
  104. package/modules/devtools.min.cjs +1 -1
  105. package/modules/devtools.min.js +1 -1
  106. package/modules/dhtmlx-compat.d.ts +1 -1
  107. package/modules/dhtmlx-compat.esm.min.js +3 -3
  108. package/modules/dhtmlx-compat.min.cjs +3 -3
  109. package/modules/dhtmlx-compat.min.js +3 -3
  110. package/modules/gantt.d.ts +563 -12
  111. package/modules/gantt.esm.min.js +8 -4
  112. package/modules/gantt.min.cjs +8 -4
  113. package/modules/gantt.min.js +8 -4
  114. package/modules/geo-europe-nuts.d.ts +1 -1
  115. package/modules/geo-europe-nuts.esm.min.js +1 -1
  116. package/modules/geo-uk.d.ts +1 -1
  117. package/modules/geo-uk.esm.min.js +1 -1
  118. package/modules/geo-us-states.d.ts +1 -1
  119. package/modules/geo-us-states.esm.min.js +1 -1
  120. package/modules/geo-world-110m.d.ts +1 -1
  121. package/modules/geo-world-110m.esm.min.js +1 -1
  122. package/modules/geo-world-50m.d.ts +1 -1
  123. package/modules/geo-world-50m.esm.min.js +1 -1
  124. package/modules/htmx.d.ts +1 -1
  125. package/modules/htmx.esm.min.js +197 -39
  126. package/modules/htmx.min.cjs +197 -39
  127. package/modules/htmx.min.js +197 -39
  128. package/modules/kanban.d.ts +687 -4
  129. package/modules/kanban.esm.min.js +3 -3
  130. package/modules/kanban.min.cjs +3 -3
  131. package/modules/kanban.min.js +3 -3
  132. package/modules/kpi.d.ts +237 -3
  133. package/modules/kpi.esm.min.js +4 -4
  134. package/modules/kpi.min.cjs +4 -4
  135. package/modules/kpi.min.js +4 -4
  136. package/modules/layout.d.ts +249 -7
  137. package/modules/layout.esm.min.js +3 -3
  138. package/modules/layout.min.cjs +3 -3
  139. package/modules/layout.min.js +3 -3
  140. package/modules/mock-socket.d.ts +6 -1
  141. package/modules/mock-socket.esm.min.js +1 -1
  142. package/modules/mock-socket.min.cjs +1 -1
  143. package/modules/mock-socket.min.js +1 -1
  144. package/modules/react.d.ts +18 -1
  145. package/modules/react.esm.min.js +3 -3
  146. package/modules/react.min.cjs +3 -3
  147. package/modules/react.min.js +3 -3
  148. package/modules/svelte.d.ts +1 -1
  149. package/modules/svelte.esm.min.js +3 -3
  150. package/modules/svelte.min.cjs +3 -3
  151. package/modules/svelte.min.js +3 -3
  152. package/modules/tabs.d.ts +116 -11
  153. package/modules/tabs.esm.min.js +3 -3
  154. package/modules/tabs.min.cjs +3 -3
  155. package/modules/tabs.min.js +3 -3
  156. package/modules/vue.d.ts +12 -1
  157. package/modules/vue.esm.min.js +3 -3
  158. package/modules/vue.min.cjs +3 -3
  159. package/modules/vue.min.js +3 -3
  160. package/modules/webcomponent.d.ts +89 -6
  161. package/modules/webcomponent.esm.min.js +242 -43
  162. package/modules/webcomponent.min.cjs +242 -43
  163. package/modules/webcomponent.min.js +242 -43
  164. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, charts module
2
+ * Lattice Grid 1.68.2, charts module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -1608,12 +1608,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
1608
1608
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
1609
1609
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
1610
1610
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
1611
- const STAMPED_VERSION="1.68.0";
1611
+ const STAMPED_VERSION="1.68.2";
1612
1612
  async function resolveVersion(){
1613
1613
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
1614
1614
  return STAMPED_VERSION;
1615
1615
  }
1616
- const VERSION="1.68.0";
1616
+ const VERSION="1.68.2";
1617
1617
  const warned=new Set();
1618
1618
  const WARNED_LIMIT=2000;
1619
1619
  function rememberWarned(key){
@@ -3966,10 +3966,18 @@ size:type_.small,
3966
3966
  if(axes){
3967
3967
  drawAxisLines({group:ctx.groups.rules,plot});
3968
3968
  drawAxisTitle({
3969
- group:ctx.groups.axis,plot,text:axisConfig.y.title,side:'left',size:type_.axisTitle,
3969
+ group:ctx.groups.axis,
3970
+ plot,
3971
+ text:axisConfig.y.title,
3972
+ side:horizontal?'bottom':'left',
3973
+ size:type_.axisTitle,
3970
3974
  });
3971
3975
  drawAxisTitle({
3972
- group:ctx.groups.axis,plot,text:axisConfig.x.title,side:'bottom',size:type_.axisTitle,
3976
+ group:ctx.groups.axis,
3977
+ plot,
3978
+ text:axisConfig.x.title,
3979
+ side:horizontal?'left':'bottom',
3980
+ size:type_.axisTitle,
3973
3981
  });
3974
3982
  }
3975
3983
  if(axes&&rightScale){
@@ -6386,14 +6394,12 @@ const columns=ctx.columns||[];
6386
6394
  if(columns.length<2)return null;
6387
6395
  const stats=ctx.grid&&ctx.grid.statistics;
6388
6396
  if(!stats)return null;
6389
- const spearman=ctx.method==='spearman';
6397
+ const method=ctx.method==='spearman'?'spearman'
6398
+ :ctx.method==='kendall'?'kendall':'correlation';
6390
6399
  const cells=[];
6391
6400
  for(let row=0;row<columns.length;row++){
6392
6401
  for(let col=0;col<columns.length;col++){
6393
- const r=row===col
6394
- ?1
6395
- :(spearman?stats.spearman(columns[row],columns[col])
6396
- :stats.correlation(columns[row],columns[col]));
6402
+ const r=row===col?1:stats[method](columns[row],columns[col]);
6397
6403
  cells.push({row,col,a:columns[row],b:columns[col],r});
6398
6404
  }
6399
6405
  }
@@ -9462,6 +9468,7 @@ Object.defineProperty(__exports,"canChartRange",{enumerable:true,get:function(){
9462
9468
  const __m0=__req("packages/modules/charts/cartesian.js");
9463
9469
  const aheadOfWindow=__m0["aheadOfWindow"];
9464
9470
  const axisWindowSpan=__m0["axisWindowSpan"];
9471
+ const CARTESIAN=__m0["CARTESIAN"];
9465
9472
  const drawCartesian=__m0["drawCartesian"];
9466
9473
  const drawForest=__m0["drawForest"];
9467
9474
  const nearestCategory=__m0["nearestCategory"];
@@ -10236,6 +10243,7 @@ axis:brush.axis,
10236
10243
  from,
10237
10244
  to,
10238
10245
  });
10246
+ let prevented=false;
10239
10247
  const detail=this.emit('brush',{
10240
10248
  mode,
10241
10249
  kind:result.kind,
@@ -10245,8 +10253,9 @@ axis:result.axis,
10245
10253
  column:(result.axis==='y'||result.axis==='y2')
10246
10254
  ?((this.#bound.measure&&this.#bound.measure.col)||null)
10247
10255
  :this.#bound.dimension.col,
10256
+ preventDefault(){prevented=true;},
10248
10257
  });
10249
- if(detail.defaultPrevented)return;
10258
+ if(prevented||detail.defaultPrevented)return;
10250
10259
  if(mode==='zoom'){
10251
10260
  if(result.range){
10252
10261
  const key=result.axis==='y2'?'y2':(result.axis==='y'?'y':'x');
@@ -10570,11 +10579,9 @@ height,
10570
10579
  fontSize,
10571
10580
  margin:this.#spec.margin,
10572
10581
  titleSize:typography.axisTitle,
10573
- titles:{
10574
- left:axis.y.title,
10575
- bottom:axis.x.title,
10576
- right:axis.y2.title,
10577
- },
10582
+ titles:(CARTESIAN[type]&&CARTESIAN[type].horizontal)
10583
+ ?{left:axis.x.title,bottom:axis.y.title,right:axis.y2.title}
10584
+ :{left:axis.y.title,bottom:axis.x.title,right:axis.y2.title},
10578
10585
  rightGutter:this.#hasRightAxis(type,bound)?Math.round(fontSize*3):0,
10579
10586
  yLabels:leftLabelsFor(type,bound,this.#grid,this.#spec),
10580
10587
  xLabels:SIDE_LABELLED.has(type)?[]:bound.labels,
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, data-router module type declarations
2
+ * Lattice Grid 1.68.2, data-router module type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -53,7 +53,16 @@ type RouteWhere = Record<string, unknown>;
53
53
  * shorthand (`sum`, `avg`, `min`, `max`, `count`).
54
54
  */
55
55
  interface RouteRollup {
56
+ /**
57
+ * What identifies a group: a property name, a `fn(row)`, or a list of either for a
58
+ * composite. Named properties are carried onto the summary row.
59
+ */
56
60
  groupBy: RouterKey | RouterKey[];
61
+ /**
62
+ * The summary fields, each a reducer over the group's rows: a function handed the rows,
63
+ * or `{ op, field }` for `count`, `sum`, `avg`, `min` or `max` over the numeric values
64
+ * of `field`. `avg` over no numeric values is 0; `min` and `max` are undefined.
65
+ */
57
66
  aggregate?: Record<string, ((rows: RouterRecord[]) => unknown) | { op: string; field?: string }>;
58
67
  }
59
68
 
@@ -81,16 +90,70 @@ type RouterWrite = Record<string, unknown>;
81
90
  * refresh under load (v13).
82
91
  */
83
92
  interface RouteOptions {
93
+ /**
94
+ * What identifies a row on this route — a field name or a `fn(row)` — overriding the
95
+ * router's own `rowKey`.
96
+ */
84
97
  rowKey?: RouterKey;
98
+ /**
99
+ * Reshapes each row before the viewer sees it. The row's identity is still taken from
100
+ * the original, so the keyed diff is unaffected; a transform route is derived and its
101
+ * edits are always reverted.
102
+ */
85
103
  transform?: (row: RouterRecord) => RouterRecord;
104
+ /**
105
+ * Admits a subset of the route's partition to the viewer. It runs on the original row,
106
+ * before any transform and before a rollup groups them.
107
+ */
86
108
  filter?: (row: RouterRecord) => boolean;
109
+ /**
110
+ * Orders the rows the viewer receives — a comparator, or `{ key, dir }`. A viewer with
111
+ * a sort model of its own still governs the final display; for a rollup route this
112
+ * sorts the summary rows.
113
+ */
87
114
  sort?: RouteSort;
115
+ /**
116
+ * Feeds the viewer one summary row per group instead of the raw rows. A rollup route is
117
+ * derived and its edits are always reverted.
118
+ */
88
119
  rollup?: RouteRollup;
120
+ /**
121
+ * A filter-wire condition for this route's slice, read only by `query()`: the pushable
122
+ * part goes to the engine and the rest is finished in the browser. It has no effect on
123
+ * rows arriving through `load`, `apply` or `push`.
124
+ */
89
125
  where?: RouteWhere;
126
+ /**
127
+ * Captures the viewer's committed edits and routes them to `onWrite` instead of leaving
128
+ * them local. Defaults to false; on a derived (rollup or transform) route the edit is
129
+ * reverted with a warning whatever this says.
130
+ */
90
131
  writable?: boolean;
132
+ /**
133
+ * Receives each captured edit as `{ key, colId, value, before, row }`. Return `false`
134
+ * or `{ ok: false }` (or reject, or throw) to revert the cell, `{ conflict, row }` to
135
+ * fire `onConflict` and take the winning row, anything else to accept; a promise is
136
+ * awaited with the optimistic value standing. Overrides the router-wide handler. The
137
+ * context's `route` is the viewer, and `source` is the `addSource` handle of the feed
138
+ * the edited row arrived on — the one to persist back to on a fan-in router — or null
139
+ * when the rows were loaded into the router directly.
140
+ */
91
141
  onWrite?: (change: RouterWrite, ctx: { route: unknown; source: unknown }) => unknown;
142
+ /**
143
+ * Called when `onWrite` returns a `conflict`, with the server's row, so the host can
144
+ * tell the user. The router itself is last-write-wins: the returned row (or the
145
+ * optimistic one) re-enters regardless. Overrides the router-wide handler.
146
+ */
92
147
  onConflict?: (change: RouterWrite, ctx: { serverRow: RouterRecord }) => void;
148
+ /**
149
+ * Names this route in `metrics()` and the devtools panel. Defaults to null — the
150
+ * default route reports `default`.
151
+ */
93
152
  label?: string;
153
+ /**
154
+ * Throttles how often this route's viewer is refreshed under load. Omitted, every
155
+ * change repaints the viewer at once.
156
+ */
94
157
  backpressure?: RouteBackpressure;
95
158
  }
96
159
 
@@ -120,9 +183,26 @@ interface RouterEdge { from: unknown; to: unknown; on?: SelectionRelation; relat
120
183
  * spec. Desugars to those calls and composes with them.
121
184
  */
122
185
  interface RouterConfig {
186
+ /**
187
+ * The routes to open, each `{ grid, when, ...routeOptions }`, or `{ default: grid }`,
188
+ * or `{ subscribe: handler, when }`, or `{ alert: handler, when, condition }`. `when`
189
+ * is the partition value or predicate `attach` takes.
190
+ */
123
191
  routes?: Record<string, unknown>[];
192
+ /**
193
+ * Selection links to make, each naming the `from` and `to` grids and the relation as
194
+ * `on` (or `relation`) — the arguments of `link()`.
195
+ */
124
196
  links?: { from: unknown; to: unknown; on?: SelectionRelation; relation?: SelectionRelation }[];
197
+ /**
198
+ * Relationship-graph edges to register, as `relate()` takes them; an edge may be
199
+ * `mutual` to work in both directions.
200
+ */
125
201
  relate?: RouterEdge[];
202
+ /**
203
+ * Turns on the time-travel buffer, bounded by `window` (ms of feed time) and/or `max`
204
+ * deltas. An empty object applies a default cap of 10,000 deltas and warns.
205
+ */
126
206
  buffer?: { window?: number; max?: number };
127
207
  }
128
208
 
@@ -136,13 +216,37 @@ interface RouterConfig {
136
216
  * `passthrough` it unjoined, or fill the fields with `null`.
137
217
  */
138
218
  interface RouterJoin {
219
+ /** The id of the registered source holding the lookup rows. */
139
220
  from: string;
221
+ /**
222
+ * Reads the joining value off this source's row — a field name or a `fn(row)`.
223
+ * Required: without it (or `on`) the join is ignored with a warning.
224
+ */
140
225
  localKey?: RouterKey;
226
+ /** An alias for `localKey`, read when `localKey` is absent. */
141
227
  on?: RouterKey;
228
+ /**
229
+ * Reads the joining value off the lookup row — a field name or a `fn(row)`. Defaults to
230
+ * a string `localKey`; with a function `localKey` and no `foreignKey`, the join is
231
+ * ignored with a warning.
232
+ */
142
233
  foreignKey?: RouterKey;
234
+ /** An alias for `foreignKey`, read when `foreignKey` is absent. */
143
235
  fromKey?: RouterKey;
236
+ /**
237
+ * Which lookup fields to carry onto the row: a list of names, a `{ from: to }` rename
238
+ * map, or a `fn(lookupRow, leftRow)` returning the fields to merge. With none, rows
239
+ * pass through unenriched and the router warns.
240
+ */
144
241
  fields?: string[] | Record<string, string> | ((lookupRow: RouterRecord | null, leftRow: RouterRecord) => RouterRecord);
242
+ /** An alias for `fields`, read when `fields` is absent. */
145
243
  select?: string[] | Record<string, string> | ((lookupRow: RouterRecord | null, leftRow: RouterRecord) => RouterRecord);
244
+ /**
245
+ * What happens while the lookup row has not arrived: `hold` keeps the row from viewers
246
+ * until it does, `passthrough` sends it unenriched, `null` fills the declared fields
247
+ * with null. Defaults to `passthrough`, which an unrecognised value also falls back to,
248
+ * with a warning.
249
+ */
146
250
  missing?: 'hold' | 'passthrough' | 'null';
147
251
  }
148
252
 
@@ -178,10 +282,24 @@ interface RouterSourceHandle {
178
282
 
179
283
  /** One route's figures in a `metrics()` snapshot (v10). */
180
284
  interface RouterRouteMetrics {
285
+ /** The route's `label`, or `default` for the default route, or null when it has neither. */
181
286
  label: string | null;
287
+ /** How many rows the route's partition holds, before its filter, links and rollup. */
182
288
  rows: number;
289
+ /**
290
+ * How many rows the viewer currently holds — the partition after the route's filter,
291
+ * any cross-grid links, and a rollup's grouping.
292
+ */
183
293
  shown: number;
294
+ /**
295
+ * Rows routed to this route per second since the previous metrics read. The first read
296
+ * of a route reports 0, having no interval to measure.
297
+ */
184
298
  throughput: number;
299
+ /**
300
+ * Any further counter the route's own stage publishes. The four above are the ones
301
+ * every route reports; a stage may add to them and they arrive here.
302
+ */
185
303
  [key: string]: unknown;
186
304
  }
187
305
 
@@ -194,12 +312,31 @@ interface RouterSourceMetrics { id: string; rows: number; throughput: number; [k
194
312
  * dropped (duplicate), buffered and lag figures.
195
313
  */
196
314
  interface RouterMetrics {
315
+ /** One entry per route, in attach order, the default route last. */
197
316
  routes: RouterRouteMetrics[];
317
+ /** One entry per registered fan-in source. */
198
318
  sources: RouterSourceMetrics[];
319
+ /**
320
+ * How many arriving records matched no route (and went to the default sink, if there is
321
+ * one). Counted since the last `load()` or `query()`, each of which resets it.
322
+ */
199
323
  unrouted: number;
324
+ /**
325
+ * How many deltas the dedupe gate discarded as stale or already seen, cumulative for
326
+ * the router's life.
327
+ */
200
328
  dropped: number;
329
+ /** How many deltas the time-travel ring currently holds. Zero when not buffering. */
201
330
  buffered: number;
331
+ /**
332
+ * How many buffered deltas the viewers are behind the live head, in deltas. Nonzero
333
+ * only while scrubbed into the past.
334
+ */
202
335
  lag: number;
336
+ /**
337
+ * Rows routed across every route per second since the previous metrics read; 0 on the
338
+ * first read.
339
+ */
203
340
  throughput: number;
204
341
  }
205
342
 
@@ -220,7 +357,17 @@ interface RouterDevtoolsPanel { refresh(): void; destroy(): void }
220
357
  * `capabilities` the planner consults to decide what it may push down.
221
358
  */
222
359
  interface RouterQueryAdapter {
360
+ /**
361
+ * What the engine can evaluate, as the pushdown capability model reads it; the planner
362
+ * consults it to decide how much of a filter to push down. Omitted, the conservative
363
+ * defaults apply.
364
+ */
223
365
  capabilities?: Record<string, unknown>;
366
+ /**
367
+ * Runs one planned query and resolves to its rows (`total` optional and unused by the
368
+ * router). Called once per `where` route, plus once for the base query shared by the
369
+ * routes without one.
370
+ */
224
371
  execute: (query: Record<string, unknown>, request?: Record<string, unknown>) => Promise<{ rows: RouterRecord[]; total?: number }>;
225
372
  }
226
373
 
@@ -230,14 +377,46 @@ interface RouterQueryAdapter {
230
377
  * `indexedDB` / `dbName` / `storeName` select the browser store.
231
378
  */
232
379
  interface RouterPersistOptions {
380
+ /** The record the snapshot is written under. Defaults to `lattice-router`. */
233
381
  key?: string;
382
+ /**
383
+ * How long to wait after a change before writing, in ms, so a burst costs one write.
384
+ * Defaults to 250; zero or less writes on every change.
385
+ */
234
386
  debounce?: number;
387
+ /** An async key/value backend of your own. Given one, IndexedDB is never opened. */
235
388
  storage?: { get: (key: string) => Promise<unknown>; set: (key: string, value: unknown) => Promise<void> };
389
+ /**
390
+ * The `IDBFactory` to open the database with. Defaults to the global `indexedDB`; where
391
+ * none is reachable the router warns once and keeps running in memory with no durable
392
+ * resume.
393
+ */
236
394
  indexedDB?: unknown;
395
+ /** The IndexedDB database to open. Defaults to `lattice-router`. */
237
396
  dbName?: string;
397
+ /** The object store inside the database. Defaults to `snapshots`. */
238
398
  storeName?: string;
239
399
  }
240
400
 
401
+ /**
402
+ * The events a data router raises.
403
+ *
404
+ * One event, and the router raises nothing else: routing itself is reported to
405
+ * each attached viewer through its own `rows.apply`, not through an event here.
406
+ * The `metrics` timer runs only while at least one `metrics` listener is
407
+ * registered, so collection costs nothing until someone asks for it, and stops
408
+ * when the last listener unsubscribes.
409
+ */
410
+ type RouterEventName =
411
+ /** The metrics timer fired: a `metrics()` snapshot, every `metricsInterval` ms (default 1000; `0` disables the timer). */
412
+ | 'metrics';
413
+
414
+ /** What a handler receives, per router event. */
415
+ interface RouterEventPayloads {
416
+ /** The same snapshot `metrics()` returns, taken at the emit; the throughput baseline advances with it. */
417
+ metrics: RouterMetrics;
418
+ }
419
+
241
420
  /**
242
421
  * A data router: one arriving stream, partitioned by a property (or composite
243
422
  * predicate), fanned out to a grid per partition. Each grid
@@ -287,8 +466,12 @@ interface DataRouter {
287
466
  sources(): string[];
288
467
  /** A cheap point-in-time snapshot of the router's runtime (v10); throughput is measured since the previous read. */
289
468
  metrics(): RouterMetrics;
290
- /** Subscribe to the periodic `metrics` emit (v10) — the only event; the timer runs only while a listener is registered. Returns the unsubscribe. */
291
- on(event: 'metrics', handler: (snapshot: RouterMetrics) => void): () => void;
469
+ /**
470
+ * Subscribe to the periodic `metrics` emit (v10) — the only event; the timer runs only
471
+ * while a listener is registered. What it carries is {@link RouterEventPayloads}.
472
+ * Returns the unsubscribe.
473
+ */
474
+ on(event: RouterEventName, handler: (snapshot: RouterEventPayloads[RouterEventName]) => void): () => void;
292
475
  /** Mount the live devtools panel into `el` (v10); it re-renders on each `metrics` emit. */
293
476
  mountDevtools(el: unknown): RouterDevtoolsPanel;
294
477
  /** How many records matched no route since the last `load` or `query`, running for deltas. */
@@ -356,20 +539,93 @@ interface DataRouter {
356
539
  * disables the timer) (v10).
357
540
  */
358
541
  interface DataRouterOptions {
542
+ /**
543
+ * How a record says which partition it belongs to: a property name, or a
544
+ * function of the record. This is the one thing the router needs to route.
545
+ */
359
546
  key?: RouterKey;
547
+ /**
548
+ * A record's identity within a route — a property name or a function. It
549
+ * is what makes an update an update rather than a second row, and what
550
+ * lets a record whose partition changed *move* between routes instead of
551
+ * being duplicated. Defaults to the record's `rowKey` property.
552
+ */
360
553
  rowKey?: RouterKey;
554
+ /**
555
+ * Send a record to every route whose predicate it matches, rather than to
556
+ * the first one only. Off by default.
557
+ */
361
558
  overlap?: boolean;
559
+ /**
560
+ * Called with each record that matched no route. They are never silently
561
+ * dropped: they are counted as well, and go to the default route when one
562
+ * is attached.
563
+ */
362
564
  onUnrouted?: (item: RouterRecord | RouterDelta) => void;
565
+ /**
566
+ * How long, in milliseconds, to wait before re-filtering linked grids
567
+ * after a selection changes. 16 by default; `0` re-filters synchronously.
568
+ */
363
569
  selectionDebounce?: number;
570
+ /**
571
+ * Where a record carries its version or sequence number — a property name
572
+ * or a function. Supplying it lets the router apply a feed in order and,
573
+ * unless `dedupe` says otherwise, drop a delta it has already seen.
574
+ */
364
575
  seq?: RouterKey;
576
+ /**
577
+ * Whether to drop a record whose sequence number is not newer than the
578
+ * last one applied for that identity. On whenever `seq` is given;
579
+ * meaningless without it.
580
+ */
365
581
  dedupe?: boolean;
582
+ /**
583
+ * Buffer incoming records and apply them on an interval rather than one at
584
+ * a time: a number of milliseconds, or `{ intervalMs }`. Batching also
585
+ * turns coalescing on.
586
+ */
366
587
  batch?: number | { intervalMs: number };
588
+ /**
589
+ * Settle repeated updates to the same identity inside one batch into a
590
+ * single apply, so a fast feed costs one update per row rather than one
591
+ * per message.
592
+ */
367
593
  coalesce?: boolean;
594
+ /**
595
+ * Where a record carries its timestamp — a property name or a function. It
596
+ * is the axis `scrubTo` and `replay` move along when time-travel is
597
+ * buffering.
598
+ */
368
599
  time?: RouterKey;
600
+ /**
601
+ * The clock the router stamps and expires by. `Date.now` unless you supply
602
+ * one, which is how a test drives time without faking the global.
603
+ */
369
604
  now?: () => number;
605
+ /**
606
+ * A whole router described as data — routes, links, buffering — applied
607
+ * through `configure()` as soon as the router is built.
608
+ */
370
609
  config?: RouterConfig;
610
+ /**
611
+ * Persist an edit committed in any route attached `{ writable: true }`
612
+ * that names no handler of its own. Return, or resolve, falsely to revert
613
+ * the edit; a route with neither this nor its own handler leaves the edit
614
+ * in place unpersisted and warns. The context's `source` is the
615
+ * `addSource` handle of the feed the edited row arrived on, or null.
616
+ */
371
617
  onWrite?: (change: RouterWrite, ctx: { route: unknown; source: unknown }) => unknown;
618
+ /**
619
+ * Called when a write comes back reporting a conflict, with the server's
620
+ * version of the row. The router itself is last-write-wins; this is where
621
+ * a host resolves it differently.
622
+ */
372
623
  onConflict?: (change: RouterWrite, ctx: { serverRow: RouterRecord }) => void;
624
+ /**
625
+ * How often, in milliseconds, to emit the `metrics` event. 1000 by
626
+ * default; `0` stops the timer and leaves `metrics()` to be read on
627
+ * demand.
628
+ */
373
629
  metricsInterval?: number;
374
630
  }
375
631
 
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, data-router module
2
+ * Lattice Grid 1.68.2, data-router module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -52,12 +52,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
52
52
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
53
53
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
54
54
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
55
- const STAMPED_VERSION="1.68.0";
55
+ const STAMPED_VERSION="1.68.2";
56
56
  async function resolveVersion(){
57
57
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
58
58
  return STAMPED_VERSION;
59
59
  }
60
- const VERSION="1.68.0";
60
+ const VERSION="1.68.2";
61
61
  const warned=new Set();
62
62
  const WARNED_LIMIT=2000;
63
63
  function rememberWarned(key){
@@ -2323,6 +2323,11 @@ return;
2323
2323
  metricAdd('writeAccepted',1);
2324
2324
  reenterWrite(obj&&obj.row?obj.row:change.row);
2325
2325
  };
2326
+ const sourceOf=(key)=>{
2327
+ const id=String(key);
2328
+ for(const source of sources.values())if(source.live.has(id))return source.handle;
2329
+ return null;
2330
+ };
2326
2331
  const onGridEdit=(route,e)=>{
2327
2332
  if(!e||e.key===undefined||e.colId===undefined)return;
2328
2333
  if(revertingCells.has(cellId(e.key,e.colId)))return;
@@ -2346,7 +2351,7 @@ warnOnce(
2346
2351
  return;
2347
2352
  }
2348
2353
  let result;
2349
- try{result=onWrite(change,{route:route.grid,source:null});}
2354
+ try{result=onWrite(change,{route:route.grid,source:sourceOf(change.key)});}
2350
2355
  catch{revertCell(route,change);metricAdd('writeRejected',1);return;}
2351
2356
  if(result&&typeof result.then==='function'){
2352
2357
  result.then(
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, data-router module
2
+ * Lattice Grid 1.68.2, data-router module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -54,12 +54,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
54
54
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
55
55
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
56
56
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
57
- const STAMPED_VERSION="1.68.0";
57
+ const STAMPED_VERSION="1.68.2";
58
58
  async function resolveVersion(){
59
59
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
60
60
  return STAMPED_VERSION;
61
61
  }
62
- const VERSION="1.68.0";
62
+ const VERSION="1.68.2";
63
63
  const warned=new Set();
64
64
  const WARNED_LIMIT=2000;
65
65
  function rememberWarned(key){
@@ -2325,6 +2325,11 @@ return;
2325
2325
  metricAdd('writeAccepted',1);
2326
2326
  reenterWrite(obj&&obj.row?obj.row:change.row);
2327
2327
  };
2328
+ const sourceOf=(key)=>{
2329
+ const id=String(key);
2330
+ for(const source of sources.values())if(source.live.has(id))return source.handle;
2331
+ return null;
2332
+ };
2328
2333
  const onGridEdit=(route,e)=>{
2329
2334
  if(!e||e.key===undefined||e.colId===undefined)return;
2330
2335
  if(revertingCells.has(cellId(e.key,e.colId)))return;
@@ -2348,7 +2353,7 @@ warnOnce(
2348
2353
  return;
2349
2354
  }
2350
2355
  let result;
2351
- try{result=onWrite(change,{route:route.grid,source:null});}
2356
+ try{result=onWrite(change,{route:route.grid,source:sourceOf(change.key)});}
2352
2357
  catch{revertCell(route,change);metricAdd('writeRejected',1);return;}
2353
2358
  if(result&&typeof result.then==='function'){
2354
2359
  result.then(
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, data-router module
2
+ * Lattice Grid 1.68.2, data-router module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -54,12 +54,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
54
54
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
55
55
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
56
56
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
57
- const STAMPED_VERSION="1.68.0";
57
+ const STAMPED_VERSION="1.68.2";
58
58
  async function resolveVersion(){
59
59
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
60
60
  return STAMPED_VERSION;
61
61
  }
62
- const VERSION="1.68.0";
62
+ const VERSION="1.68.2";
63
63
  const warned=new Set();
64
64
  const WARNED_LIMIT=2000;
65
65
  function rememberWarned(key){
@@ -2325,6 +2325,11 @@ return;
2325
2325
  metricAdd('writeAccepted',1);
2326
2326
  reenterWrite(obj&&obj.row?obj.row:change.row);
2327
2327
  };
2328
+ const sourceOf=(key)=>{
2329
+ const id=String(key);
2330
+ for(const source of sources.values())if(source.live.has(id))return source.handle;
2331
+ return null;
2332
+ };
2328
2333
  const onGridEdit=(route,e)=>{
2329
2334
  if(!e||e.key===undefined||e.colId===undefined)return;
2330
2335
  if(revertingCells.has(cellId(e.key,e.colId)))return;
@@ -2348,7 +2353,7 @@ warnOnce(
2348
2353
  return;
2349
2354
  }
2350
2355
  let result;
2351
- try{result=onWrite(change,{route:route.grid,source:null});}
2356
+ try{result=onWrite(change,{route:route.grid,source:sourceOf(change.key)});}
2352
2357
  catch{revertCell(route,change);metricAdd('writeRejected',1);return;}
2353
2358
  if(result&&typeof result.then==='function'){
2354
2359
  result.then(
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, devtools module type declarations
2
+ * Lattice Grid 1.68.2, devtools module type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, devtools module
2
+ * Lattice Grid 1.68.2, devtools module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, devtools module
2
+ * Lattice Grid 1.68.2, devtools module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, devtools module
2
+ * Lattice Grid 1.68.2, devtools module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, dhtmlx-compat module type declarations
2
+ * Lattice Grid 1.68.2, dhtmlx-compat module type declarations
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Lattice Grid 1.68.0, dhtmlx-compat module
2
+ * Lattice Grid 1.68.2, dhtmlx-compat module
3
3
  * Copyright (c) 2026 TOCLOCO Inc. All rights reserved.
4
4
  * https://latticegrid.dev
5
5
  */
@@ -62,12 +62,12 @@ Object.defineProperty(__exports,"frameBatched",{enumerable:true,get:function(){r
62
62
  Object.defineProperty(__exports,"settleDebounce",{enumerable:true,get:function(){return settleDebounce;}});
63
63
  Object.defineProperty(__exports,"whenIdle",{enumerable:true,get:function(){return whenIdle;}});
64
64
  Object.defineProperty(__exports,"uid",{enumerable:true,get:function(){return uid;}});
65
- const STAMPED_VERSION="1.68.0";
65
+ const STAMPED_VERSION="1.68.2";
66
66
  async function resolveVersion(){
67
67
  if(STAMPED_VERSION!=='0.0.0-source')return STAMPED_VERSION;
68
68
  return STAMPED_VERSION;
69
69
  }
70
- const VERSION="1.68.0";
70
+ const VERSION="1.68.2";
71
71
  const warned=new Set();
72
72
  const WARNED_LIMIT=2000;
73
73
  function rememberWarned(key){