@inflexa-ai/harness 0.19.2 → 0.21.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 (139) hide show
  1. package/dist/agents/conversation-agent.d.ts +26 -14
  2. package/dist/agents/conversation-agent.js +12 -23
  3. package/dist/agents/report-session-agent.d.ts +31 -5
  4. package/dist/agents/report-session-agent.js +31 -6
  5. package/dist/app/chat-turn.js +3 -1
  6. package/dist/app/message-assembly.d.ts +15 -1
  7. package/dist/app/message-assembly.js +22 -8
  8. package/dist/app/report-session-runtime.d.ts +21 -4
  9. package/dist/app/report-session-runtime.js +53 -6
  10. package/dist/app/spawn-report-session.d.ts +174 -17
  11. package/dist/app/spawn-report-session.js +233 -21
  12. package/dist/contracts/chat-events.d.ts +7 -2
  13. package/dist/contracts/message.d.ts +10 -0
  14. package/dist/contracts/report-blocks.d.ts +743 -19
  15. package/dist/contracts/report-blocks.js +186 -11
  16. package/dist/contracts/report-reference.d.ts +88 -0
  17. package/dist/contracts/report-reference.js +48 -0
  18. package/dist/index.d.ts +9 -0
  19. package/dist/index.js +20 -0
  20. package/dist/lib/chrome.d.ts +26 -6
  21. package/dist/lib/chrome.js +99 -32
  22. package/dist/lib/eyes.d.ts +57 -0
  23. package/dist/lib/eyes.js +45 -0
  24. package/dist/lib/page-capture.d.ts +14 -0
  25. package/dist/lib/page-capture.js +56 -2
  26. package/dist/literature/sources/pubmed.js +2 -1
  27. package/dist/loop/run-agent.js +58 -23
  28. package/dist/loop/tool-detail.d.ts +19 -2
  29. package/dist/loop/tool-detail.js +35 -2
  30. package/dist/memory/content-to-cortex.d.ts +3 -2
  31. package/dist/memory/content-to-cortex.js +21 -12
  32. package/dist/memory/conversation-display-replay.js +14 -7
  33. package/dist/memory/thread-history.d.ts +60 -3
  34. package/dist/memory/thread-history.js +55 -16
  35. package/dist/memory/thread-store.d.ts +11 -1
  36. package/dist/memory/thread-store.js +12 -2
  37. package/dist/prompts/conversation.d.ts +1 -1
  38. package/dist/prompts/conversation.js +23 -6
  39. package/dist/prompts/report-session.d.ts +1 -1
  40. package/dist/prompts/report-session.js +183 -9
  41. package/dist/report-model/authoring-grammar.d.ts +463 -0
  42. package/dist/report-model/authoring-grammar.js +77 -0
  43. package/dist/report-model/block-walk.d.ts +57 -14
  44. package/dist/report-model/block-walk.js +116 -11
  45. package/dist/report-model/draft-finish.d.ts +22 -4
  46. package/dist/report-model/draft-finish.js +39 -7
  47. package/dist/report-model/draft-operations.d.ts +3 -0
  48. package/dist/report-model/draft-operations.js +19 -5
  49. package/dist/report-model/draft-read.d.ts +8 -5
  50. package/dist/report-model/draft-read.js +21 -5
  51. package/dist/report-model/fixture-resolver.js +15 -4
  52. package/dist/report-model/pin-snapshot.d.ts +24 -6
  53. package/dist/report-model/pin-snapshot.js +204 -6
  54. package/dist/report-model/production-resolver.js +15 -4
  55. package/dist/report-model/reference-resolver.d.ts +50 -1
  56. package/dist/report-model/reference-resolver.js +94 -1
  57. package/dist/report-model/reference-stamp.d.ts +45 -0
  58. package/dist/report-model/reference-stamp.js +173 -0
  59. package/dist/report-model/structural-validation.d.ts +4 -1
  60. package/dist/report-model/structural-validation.js +48 -3
  61. package/dist/report-model/validate.d.ts +1 -1
  62. package/dist/report-model/validate.js +1 -1
  63. package/dist/report-render/asset-lookup.d.ts +19 -0
  64. package/dist/report-render/asset-lookup.js +37 -0
  65. package/dist/report-render/assets.d.ts +45 -1
  66. package/dist/report-render/assets.js +65 -6
  67. package/dist/report-render/chart-presets.d.ts +102 -0
  68. package/dist/report-render/chart-presets.js +205 -0
  69. package/dist/report-render/chart.d.ts +112 -10
  70. package/dist/report-render/chart.js +1164 -41
  71. package/dist/report-render/design.d.ts +99 -0
  72. package/dist/report-render/design.js +289 -107
  73. package/dist/report-render/number-format.d.ts +117 -0
  74. package/dist/report-render/number-format.js +479 -0
  75. package/dist/report-render/page.d.ts +121 -5
  76. package/dist/report-render/page.js +689 -7
  77. package/dist/report-render/references.d.ts +70 -16
  78. package/dist/report-render/references.js +54 -22
  79. package/dist/report-render/render.d.ts +23 -5
  80. package/dist/report-render/render.js +81 -22
  81. package/dist/report-render/table-data.d.ts +92 -0
  82. package/dist/report-render/table-data.js +129 -0
  83. package/dist/report-render/types.d.ts +15 -0
  84. package/dist/report-render/value-bridge.js +6 -2
  85. package/dist/report-render/views/chart-view.d.ts +12 -5
  86. package/dist/report-render/views/chart-view.js +14 -8
  87. package/dist/report-render/views/page-view.d.ts +36 -15
  88. package/dist/report-render/views/page-view.js +45 -23
  89. package/dist/report-render/views/prose.d.ts +12 -2
  90. package/dist/report-render/views/prose.js +29 -7
  91. package/dist/report-render/views/references-view.d.ts +22 -8
  92. package/dist/report-render/views/references-view.js +101 -13
  93. package/dist/report-render/views/values.d.ts +128 -13
  94. package/dist/report-render/views/values.js +199 -18
  95. package/dist/runtime/assemble.d.ts +39 -5
  96. package/dist/runtime/assemble.js +47 -6
  97. package/dist/sandbox/client.d.ts +10 -1
  98. package/dist/sandbox/create-sandbox.d.ts +9 -4
  99. package/dist/sandbox/create-sandbox.js +22 -16
  100. package/dist/sandbox/docker-client.js +10 -7
  101. package/dist/sandbox/k8s-client.js +11 -18
  102. package/dist/sandbox/mount-plan.d.ts +45 -7
  103. package/dist/sandbox/mount-plan.js +43 -5
  104. package/dist/sandbox/types.d.ts +10 -6
  105. package/dist/state/init.js +38 -9
  106. package/dist/state/report-session-state.d.ts +42 -1
  107. package/dist/state/report-session-state.js +70 -2
  108. package/dist/state/report-versions.d.ts +22 -17
  109. package/dist/state/report-versions.js +48 -35
  110. package/dist/state/snapshot-parse.js +16 -1
  111. package/dist/tasks/data-profile.d.ts +7 -4
  112. package/dist/tasks/data-profile.js +9 -9
  113. package/dist/tools/define-tool.d.ts +32 -0
  114. package/dist/tools/define-tool.js +4 -0
  115. package/dist/tools/detail-resolver.d.ts +7 -2
  116. package/dist/tools/detail-resolver.js +7 -2
  117. package/dist/tools/iterate-report.d.ts +1 -1
  118. package/dist/tools/report/preview-snapshot.js +1 -0
  119. package/dist/tools/report-authoring/authoring-tools.d.ts +15 -6
  120. package/dist/tools/report-authoring/authoring-tools.js +99 -18
  121. package/dist/tools/report-session/derive-table.d.ts +162 -0
  122. package/dist/tools/report-session/derive-table.js +459 -0
  123. package/dist/tools/report-session/examine-page.d.ts +48 -18
  124. package/dist/tools/report-session/examine-page.js +204 -24
  125. package/dist/tools/report-session/index.d.ts +2 -0
  126. package/dist/tools/report-session/index.js +2 -0
  127. package/dist/tools/report-session/list-artifacts.d.ts +97 -0
  128. package/dist/tools/report-session/list-artifacts.js +197 -0
  129. package/dist/tools/report-session/preview-report.d.ts +6 -0
  130. package/dist/tools/report-session/preview-report.js +180 -28
  131. package/dist/tools/report-session/record-version.d.ts +22 -10
  132. package/dist/tools/report-session/record-version.js +99 -14
  133. package/dist/tools/start-report-session.d.ts +115 -0
  134. package/dist/tools/start-report-session.js +179 -0
  135. package/dist/workflows/sandbox-step.d.ts +7 -4
  136. package/dist/workflows/sandbox-step.js +10 -12
  137. package/dist/workspace/paths.d.ts +50 -0
  138. package/dist/workspace/paths.js +63 -0
  139. package/package.json +2 -1
@@ -1,17 +1,33 @@
1
1
  /**
2
- * The page constants: the head references, the readiness names, and the two page-side scripts.
2
+ * The page constants: the head references, the readiness names, and the page-side scripts.
3
3
  *
4
4
  * A script here is browser source text, not module code. Thus it reads no module binding, and each value
5
- * that it needs is interpolated at build time. The style rules and the ECharts theme live in `design.ts`.
5
+ * that it needs is interpolated at build time. The style rules, the ECharts theme, and the grid theme
6
+ * parameters live in `design.ts`.
6
7
  */
7
- import { assetSource, ECHARTS_ASSET } from "./assets.js";
8
- import { ECHARTS_THEME_NAME } from "./design.js";
8
+ import { AG_GRID_ASSET, assetSource, ECHARTS_ASSET } from "./assets.js";
9
+ import { CHART_SOURCE_MEMBER, POINT_LABEL } from "./chart.js";
10
+ import { ECHARTS_THEME_NAME, GRID_HEADER_BORDER_PX, GRID_HEADER_HEIGHT_PX, GRID_MIN_COLUMN_WIDTH_PX, GRID_PRINT_ROW_CAP, GRID_ROW_HEIGHT_PX, GRID_THEME_PARAMS, GRID_TOOLTIP_DELAY_MS, GRID_VISIBLE_ROWS, } from "./design.js";
11
+ import { scriptJson } from "./script-json.js";
12
+ import { TABLE_DATA_GLOBAL } from "./table-data.js";
13
+ import { GRID_COUNT_CLASS, GRID_MOUNT_ATTRIBUTE, GRID_NOTE_CLASS, GRID_ROWS_WORD } from "./views/values.js";
9
14
  /**
10
- * The head references of the staged assets. The page loads the chart runtime from the sibling assets
11
- * directory, thus the head names no remote host. The manifest gives the staged name, thus the tag and the
12
- * stage step of the caller cannot disagree.
15
+ * The head reference of the chart runtime. The page loads it from the sibling assets directory, thus the
16
+ * head names no remote host. The manifest gives the staged name, thus the tag and the stage step of the
17
+ * caller cannot disagree.
18
+ *
19
+ * The tag is a classic script. A `file://` page refuses a module request, thus a classic script is the one
20
+ * form that loads beside the page.
13
21
  */
14
22
  export const ASSET_HEAD = `<script src="${assetSource(ECHARTS_ASSET)}"></script>`;
23
+ /**
24
+ * The head reference of the grid runtime.
25
+ *
26
+ * The bundle weighs about two megabytes, and a page with no table has nothing to build. Thus the skeleton
27
+ * writes this tag only for a page that carries table data, and the manifest still stages the file for the
28
+ * whole directory.
29
+ */
30
+ export const GRID_ASSET_HEAD = `<script src="${assetSource(AG_GRID_ASSET)}"></script>`;
15
31
  /**
16
32
  * The name of the readiness event, and the name of the window sentinel that guards a late listener. The
17
33
  * bootstrap below emits both names, thus this module owns them. A capture that waits for the page reads the
@@ -44,12 +60,196 @@ const REVEAL_GATE = "__inflexaWhenRevealed";
44
60
  * signal in that condition, thus such a page still signals and the capture returns on the signal.
45
61
  */
46
62
  const REVEAL_SETTLE_TIMEOUT_MS = 2_000;
63
+ /**
64
+ * The class that marks the navigation link of the section in view, and the bottom root margin of the
65
+ * scrollspy in percent.
66
+ *
67
+ * The design sheet holds the matching rule under the same class name. The negative bottom margin shrinks the
68
+ * observation box to the top band of the viewport. Thus the section that sits nearest the top of the page
69
+ * wins, and a tall section below it never steals the mark.
70
+ */
71
+ const NAV_ACTIVE_CLASS = "report-nav-link-active";
72
+ const SPY_BOTTOM_MARGIN_PERCENT = 70;
73
+ /**
74
+ * The client twin of the chart data derivation, as browser source text.
75
+ *
76
+ * A dense chart ships no row. Its option states, for each series, the columns of the payload that it reads,
77
+ * the transform of each column, the split that it holds, and the rows that carry a point label. This
78
+ * fragment reads those descriptors and builds the data of each series.
79
+ *
80
+ * The fragment writes its own constants, because a page script reads no module binding. Each rule here is
81
+ * the twin of one rule of the server derivation: the number read of a cell, the four transforms, the
82
+ * competition rank, the compare of a sort, and the three-way classification of a preset. A shared test
83
+ * vector runs the transforms of both sides over one set of cells, thus the two cannot drift in silence.
84
+ *
85
+ * The build reads the decoded rows of the payload. The decoder runs before this script, thus a row is a
86
+ * record and a cell reads by its column name.
87
+ */
88
+ export const CHART_SERIES_BUILDER = `function reportNumber(cell) {
89
+ if (typeof cell === "number") {
90
+ return isFinite(cell) ? cell : null;
91
+ }
92
+ if (typeof cell === "string") {
93
+ var trimmed = cell.trim();
94
+ if (trimmed === "") {
95
+ return null;
96
+ }
97
+ var parsed = Number(trimmed);
98
+ return isFinite(parsed) ? parsed : null;
99
+ }
100
+ return null;
101
+ }
102
+ function reportRank(values) {
103
+ var counts = Object.create(null);
104
+ var distinct = [];
105
+ for (var i = 0; i < values.length; i++) {
106
+ if (values[i] === null) {
107
+ continue;
108
+ }
109
+ var key = String(values[i]);
110
+ if (counts[key] === undefined) {
111
+ counts[key] = 0;
112
+ distinct.push(values[i]);
113
+ }
114
+ counts[key] += 1;
115
+ }
116
+ distinct.sort(function (a, b) {
117
+ return a - b;
118
+ });
119
+ var places = Object.create(null);
120
+ var place = 1;
121
+ for (var d = 0; d < distinct.length; d++) {
122
+ places[String(distinct[d])] = place;
123
+ place += counts[String(distinct[d])];
124
+ }
125
+ var ranked = [];
126
+ for (var r = 0; r < values.length; r++) {
127
+ ranked.push(values[r] === null ? null : places[String(values[r])]);
128
+ }
129
+ return ranked;
130
+ }
131
+ function reportTransform(cells, name) {
132
+ var values = [];
133
+ for (var i = 0; i < cells.length; i++) {
134
+ values.push(reportNumber(cells[i]));
135
+ }
136
+ if (name === "rank") {
137
+ return reportRank(values);
138
+ }
139
+ var out = [];
140
+ for (var j = 0; j < values.length; j++) {
141
+ var value = values[j];
142
+ if (value === null) {
143
+ out.push(null);
144
+ } else if (name === "log10") {
145
+ out.push(value > 0 ? Math.log10(value) : null);
146
+ } else if (name === "neg_log10") {
147
+ out.push(value > 0 ? -Math.log10(value) : null);
148
+ } else {
149
+ out.push(Math.abs(value));
150
+ }
151
+ }
152
+ return out;
153
+ }
154
+ function reportColumn(payload, index) {
155
+ var name = payload.columns[index];
156
+ var rows = payload.rows || [];
157
+ var cells = [];
158
+ for (var i = 0; i < rows.length; i++) {
159
+ var cell = rows[i][name];
160
+ cells.push(cell === undefined ? null : cell);
161
+ }
162
+ return cells;
163
+ }
164
+ function reportChannel(payload, spec) {
165
+ var cells = reportColumn(payload, spec.column);
166
+ return spec.transform === undefined ? cells : reportTransform(cells, spec.transform);
167
+ }
168
+ function reportCompare(a, b) {
169
+ var left = reportNumber(a);
170
+ var right = reportNumber(b);
171
+ if (left !== null && right !== null) {
172
+ return left - right;
173
+ }
174
+ var x = String(a);
175
+ var y = String(b);
176
+ if (x < y) {
177
+ return -1;
178
+ }
179
+ return x > y ? 1 : 0;
180
+ }
181
+ function reportCategory(rule, xValue, yValue) {
182
+ // The rule gives the place of the category, and the descriptor of a series names the same place. Thus
183
+ // the two sides compare numbers and no category name rides the page.
184
+ var x = reportNumber(xValue);
185
+ var y = reportNumber(yValue);
186
+ if (!rule || x === null || y === null) {
187
+ return -1;
188
+ }
189
+ if (y <= rule.cut) {
190
+ return 2;
191
+ }
192
+ if (x < -rule.effect) {
193
+ return 0;
194
+ }
195
+ return x > rule.effect ? 1 : 2;
196
+ }
197
+ function reportSeriesData(payload, source, rule) {
198
+ var x = reportChannel(payload, source.x);
199
+ var y = reportChannel(payload, source.y);
200
+ var group = source.group === undefined ? null : reportChannel(payload, source.group);
201
+ var labels = source.label === undefined ? null : reportColumn(payload, source.label);
202
+ var flags = Object.create(null);
203
+ var declared = source.flags || [];
204
+ for (var f = 0; f < declared.length; f++) {
205
+ flags[declared[f]] = true;
206
+ }
207
+ var points = [];
208
+ for (var r = 0; r < x.length; r++) {
209
+ if (x[r] === null || y[r] === null) {
210
+ continue;
211
+ }
212
+ if (group !== null && group[r] !== source.value) {
213
+ continue;
214
+ }
215
+ if (source.category !== undefined && reportCategory(rule, x[r], y[r]) !== source.category) {
216
+ continue;
217
+ }
218
+ points.push({ index: r, x: x[r], y: y[r] });
219
+ }
220
+ if (source.sort) {
221
+ points.sort(function (a, b) {
222
+ return reportCompare(a.x, b.x);
223
+ });
224
+ }
225
+ var data = [];
226
+ for (var p = 0; p < points.length; p++) {
227
+ var point = points[p];
228
+ var pair = source.swap ? [point.y, point.x] : [point.x, point.y];
229
+ var label = labels === null ? null : labels[point.index];
230
+ var named = label !== null && label !== undefined;
231
+ if (!named && flags[point.index] !== true) {
232
+ data.push(pair);
233
+ continue;
234
+ }
235
+ var item = { value: pair, name: named ? String(label) : String(point.x) };
236
+ if (flags[point.index] === true) {
237
+ item.label = ${JSON.stringify(POINT_LABEL)};
238
+ }
239
+ data.push(item);
240
+ }
241
+ return data;
242
+ }`;
47
243
  /**
48
244
  * The page-side script that wires each chart. It finds every chart container, reads the option JSON from
49
245
  * the sibling `<script type="application/json">` element, and initializes ECharts with the registered
50
246
  * theme. The skeleton registers the theme before this script runs. A resize handler keeps each chart
51
247
  * fit to the window.
52
248
  *
249
+ * An option that carries the data-source member holds no row. The script then reads the registered payload
250
+ * of the artifact and builds the data of each series from the descriptors. A mount whose payload the
251
+ * registry does not hold keeps its empty card, and the walk continues.
252
+ *
53
253
  * The script signals readiness when the bootstrap completes, and immediately when no chart exists. It sets
54
254
  * the `window.__inflexaThemeReady` sentinel and dispatches the `inflexa-theme-ready` event on the document.
55
255
  * A reader that captures the page keys on this signal, thus the capture returns when the page is ready and
@@ -63,6 +263,7 @@ const REVEAL_SETTLE_TIMEOUT_MS = 2_000;
63
263
  * diagnose, thus it must never stop a sibling chart and it must never withhold the readiness signal.
64
264
  */
65
265
  export const CHART_BOOTSTRAP = `(function () {
266
+ ${CHART_SERIES_BUILDER}
66
267
  function signalReady() {
67
268
  window.${THEME_READY_SENTINEL} = true;
68
269
  document.dispatchEvent(new Event(${JSON.stringify(THEME_READY_EVENT)}));
@@ -79,6 +280,7 @@ export const CHART_BOOTSTRAP = `(function () {
79
280
  whenRevealed(signalReady);
80
281
  return;
81
282
  }
283
+ var registry = window.${TABLE_DATA_GLOBAL};
82
284
  var containers = document.querySelectorAll("[data-echarts-id]");
83
285
  for (var i = 0; i < containers.length; i++) {
84
286
  var container = containers[i];
@@ -88,6 +290,19 @@ export const CHART_BOOTSTRAP = `(function () {
88
290
  }
89
291
  try {
90
292
  var option = JSON.parse(optionScript.textContent || "{}");
293
+ var source = option.${CHART_SOURCE_MEMBER};
294
+ if (source) {
295
+ // The member is no field of the chart runtime, thus it leaves the option before the runtime reads it.
296
+ delete option.${CHART_SOURCE_MEMBER};
297
+ var payload = registry && Object.prototype.hasOwnProperty.call(registry, source.payload) ? registry[source.payload] : null;
298
+ if (!payload || !payload.columns) {
299
+ continue;
300
+ }
301
+ var series = option.series || [];
302
+ for (var s = 0; s < series.length && s < source.series.length; s++) {
303
+ series[s].data = reportSeriesData(payload, source.series[s], source.rule);
304
+ }
305
+ }
91
306
  var chart = echarts.init(container, ${JSON.stringify(ECHARTS_THEME_NAME)});
92
307
  chart.setOption(option);
93
308
  } catch (cause) {
@@ -195,3 +410,470 @@ export const FADE_IN_OBSERVER = `(function () {
195
410
  start();
196
411
  }
197
412
  })();`;
413
+ /**
414
+ * The page-side script that marks the navigation link of the section in view.
415
+ *
416
+ * Each navigation anchor targets one top-level section by its id. The script resolves each anchor to its
417
+ * section, and an `IntersectionObserver` over those sections drives the active class. The observation box
418
+ * covers the top band of the viewport alone, thus the script marks the first section of that band in
419
+ * document order and exactly one link carries the class.
420
+ *
421
+ * A section that no anchor targets, and an anchor whose target is absent, drop out of the walk. Thus the
422
+ * reference band never takes the mark.
423
+ *
424
+ * A page can leave the observation box empty. At scroll 0, each section can start below the box. A short
425
+ * tail also holds the last section below the box for the whole scroll. Thus the paint step has a fallback.
426
+ * The fallback marks the last section whose top sits at the end of the box or above it. It marks the first
427
+ * section when every top sits below the box.
428
+ *
429
+ * The fallback reads each position with `getBoundingClientRect` at paint time. A scroll moves each section,
430
+ * thus a cached position names the wrong link. As a result, exactly one link carries the active class on a
431
+ * page that has a section.
432
+ *
433
+ * The highlight is decoration. A browser with no `IntersectionObserver`, and a page with no navigation,
434
+ * keep the plain links. The script writes one class and it reads nothing that the reveal script writes,
435
+ * thus the two observers never contend.
436
+ */
437
+ export const SECTION_SPY = `(function () {
438
+ function start() {
439
+ var links = document.querySelectorAll(".report-nav-link");
440
+ if (typeof IntersectionObserver === "undefined" || links.length === 0) {
441
+ return;
442
+ }
443
+ var anchors = [];
444
+ var sections = [];
445
+ for (var i = 0; i < links.length; i++) {
446
+ var href = links[i].getAttribute("href") || "";
447
+ var section = href.charAt(0) === "#" ? document.getElementById(href.slice(1)) : null;
448
+ if (!section) {
449
+ continue;
450
+ }
451
+ anchors.push(links[i]);
452
+ sections.push(section);
453
+ }
454
+ if (sections.length === 0) {
455
+ return;
456
+ }
457
+ var inBand = [];
458
+ for (var s = 0; s < sections.length; s++) {
459
+ inBand.push(false);
460
+ }
461
+ function boxEnd() {
462
+ var root = document.documentElement;
463
+ var height = root ? root.clientHeight : 0;
464
+ return (height * (100 - ${SPY_BOTTOM_MARGIN_PERCENT})) / 100;
465
+ }
466
+ function nearestAbove() {
467
+ var end = boxEnd();
468
+ var last = -1;
469
+ for (var n = 0; n < sections.length; n++) {
470
+ if (sections[n].getBoundingClientRect().top <= end) {
471
+ last = n;
472
+ }
473
+ }
474
+ return last < 0 ? 0 : last;
475
+ }
476
+ function paint() {
477
+ var active = -1;
478
+ for (var a = 0; a < inBand.length; a++) {
479
+ if (inBand[a]) {
480
+ active = a;
481
+ break;
482
+ }
483
+ }
484
+ if (active < 0) {
485
+ active = nearestAbove();
486
+ }
487
+ for (var b = 0; b < anchors.length; b++) {
488
+ if (b === active) {
489
+ anchors[b].classList.add(${JSON.stringify(NAV_ACTIVE_CLASS)});
490
+ } else {
491
+ anchors[b].classList.remove(${JSON.stringify(NAV_ACTIVE_CLASS)});
492
+ }
493
+ }
494
+ }
495
+ var observer = new IntersectionObserver(function (entries) {
496
+ for (var e = 0; e < entries.length; e++) {
497
+ var index = sections.indexOf(entries[e].target);
498
+ if (index >= 0) {
499
+ inBand[index] = entries[e].isIntersecting === true;
500
+ }
501
+ }
502
+ paint();
503
+ }, { rootMargin: "0px 0px -${SPY_BOTTOM_MARGIN_PERCENT}% 0px", threshold: 0 });
504
+ for (var k = 0; k < sections.length; k++) {
505
+ observer.observe(sections[k]);
506
+ }
507
+ }
508
+ if (document.readyState === "loading") {
509
+ document.addEventListener("DOMContentLoaded", start);
510
+ } else {
511
+ start();
512
+ }
513
+ })();`;
514
+ /**
515
+ * The decoder of the table data assets.
516
+ *
517
+ * Each data asset registers its payload under the global map, keyed by the block id. The payload is
518
+ * columnar: a row is an array in column order, and a number in a column that carries a dictionary is the
519
+ * index of its value. This script materializes the decoded rows one time, thus a reader of the data takes
520
+ * plain row objects and no reader repeats the decode.
521
+ *
522
+ * The decoded rows land on the payload itself, under `rows`. The encoded arrays stay under `encoded`, thus
523
+ * the decode runs one time even when the script runs again. A cell of `null` names a column that the row
524
+ * does not hold, and the decoded row omits that key. Thus a ragged row stays ragged and no key reads as an
525
+ * empty value.
526
+ *
527
+ * The script runs after the data assets and before the page scripts. A page with no table registers no
528
+ * map, and the guard leaves such a page untouched.
529
+ */
530
+ export const TABLE_DATA_DECODER = `(function () {
531
+ var registry = window.${TABLE_DATA_GLOBAL};
532
+ if (!registry) {
533
+ return;
534
+ }
535
+ var ids = Object.keys(registry);
536
+ for (var i = 0; i < ids.length; i++) {
537
+ var payload = registry[ids[i]];
538
+ if (!payload || payload.encoded) {
539
+ continue;
540
+ }
541
+ var columns = payload.columns || [];
542
+ var dict = payload.dict || {};
543
+ var encoded = payload.rows || [];
544
+ var decoded = [];
545
+ for (var r = 0; r < encoded.length; r++) {
546
+ var row = encoded[r];
547
+ // A column name is authored text. A plain object would read an inherited member such as
548
+ // "constructor" as a dictionary, and it would send a "__proto__" column to the prototype. The
549
+ // null-prototype record and the own-key test keep every column name an ordinary entry.
550
+ var record = Object.create(null);
551
+ for (var c = 0; c < columns.length; c++) {
552
+ var cell = row[c];
553
+ if (cell === null || cell === undefined) {
554
+ continue;
555
+ }
556
+ var name = columns[c];
557
+ var values = Object.prototype.hasOwnProperty.call(dict, name) ? dict[name] : null;
558
+ record[name] = values && typeof cell === "number" ? values[cell] : cell;
559
+ }
560
+ decoded.push(record);
561
+ }
562
+ payload.encoded = encoded;
563
+ payload.rows = decoded;
564
+ }
565
+ })();`;
566
+ /**
567
+ * The client twin of `formatTableCell`, as browser source text.
568
+ *
569
+ * The rows of a table reach the reader through the data asset, thus the page formats each cell. The server
570
+ * ships the kind of each column, and this fragment reads the cell under that kind: the identifier text, the
571
+ * bound of a stored zero, the exponent of a small probability, the grouped whole number, the rounded float,
572
+ * the typographic minus of a negative, and the trim of a delimited name.
573
+ *
574
+ * The fragment writes its own constants, because a page script reads no module binding. A shared test vector
575
+ * runs the server helper and this twin over the same entries, thus the two cannot give different text in
576
+ * silence. `formatCell` is the one entry point, and the grid boot below inlines the whole fragment.
577
+ */
578
+ export const TABLE_CELL_FORMATTER = `function formatCell(cell, kind, bound) {
579
+ var text = cellText(cell, kind, bound);
580
+ var segments = text.split("%");
581
+ if (segments.length < 3) {
582
+ return text;
583
+ }
584
+ for (var s = 0; s < segments.length; s++) {
585
+ if (segments[s].length === 0 || /\\s/.test(segments[s])) {
586
+ return text;
587
+ }
588
+ }
589
+ return segments[0];
590
+ }
591
+ function cellText(cell, kind, bound) {
592
+ if (kind === "identifier") {
593
+ return String(cell).trim();
594
+ }
595
+ var value = finiteValue(cell);
596
+ if (value === null) {
597
+ return String(cell);
598
+ }
599
+ return shownMinus(numberText(value, kind, bound));
600
+ }
601
+ function shownMinus(text) {
602
+ // The leading character alone. An exponent keeps its own hyphen, thus one form reads one notation.
603
+ return text.charAt(0) === "-" ? "−" + text.slice(1) : text;
604
+ }
605
+ function numberText(value, kind, bound) {
606
+ if (kind === "scientific") {
607
+ if (value === 0) {
608
+ return bound !== undefined && bound !== null && bound > 0 ? "<" + boundForm(bound) : "≈" + "0";
609
+ }
610
+ if (value > 0 && value < 1e-2) {
611
+ return scientificForm(value);
612
+ }
613
+ }
614
+ if (Number.isSafeInteger(value)) {
615
+ return compactForm(value);
616
+ }
617
+ var magnitude = Math.abs(value);
618
+ if (value !== 0 && magnitude < 1e-3) {
619
+ return scientificForm(value);
620
+ }
621
+ var rounded = Math.round(magnitude);
622
+ if (rounded >= 1e3 && rounded < 1e15) {
623
+ return compactForm(value);
624
+ }
625
+ return tidy(value.toPrecision(3));
626
+ }
627
+ function finiteValue(cell) {
628
+ if (typeof cell === "number") {
629
+ return isFinite(cell) ? cell : null;
630
+ }
631
+ var trimmed = String(cell).trim();
632
+ if (trimmed === "") {
633
+ return null;
634
+ }
635
+ var parsed = Number(trimmed);
636
+ return isFinite(parsed) ? parsed : null;
637
+ }
638
+ function boundForm(bound) {
639
+ var parts = bound.toExponential().split("e");
640
+ var digits = parts[0].replace(".", "");
641
+ var raised = Number(digits.charAt(0)) + (digits.length > 1 ? 1 : 0);
642
+ var carries = raised === 10;
643
+ var digit = carries ? 1 : raised;
644
+ var power = Number(parts[1]) + (carries ? 1 : 0);
645
+ if (power < -2) {
646
+ return digit + "e" + power;
647
+ }
648
+ return power >= 0 ? String(digit) + zeros(power) : "0." + zeros(-power - 1) + digit;
649
+ }
650
+ function zeros(count) {
651
+ var text = "";
652
+ for (var z = 0; z < count; z++) {
653
+ text += "0";
654
+ }
655
+ return text;
656
+ }
657
+ function scientificForm(value) {
658
+ return tidy(value.toExponential(1));
659
+ }
660
+ function compactForm(value) {
661
+ return (value < 0 ? "-" : "") + groupDigits(Math.abs(value).toFixed(0));
662
+ }
663
+ function groupDigits(digits) {
664
+ if (!/^[0-9]+$/.test(digits)) {
665
+ return digits;
666
+ }
667
+ var grouped = "";
668
+ for (var d = 0; d < digits.length; d++) {
669
+ if (d > 0 && (digits.length - d) % 3 === 0) {
670
+ grouped += ",";
671
+ }
672
+ grouped += digits.charAt(d);
673
+ }
674
+ return grouped;
675
+ }
676
+ function tidy(text) {
677
+ var marker = text.indexOf("e");
678
+ if (marker < 0) {
679
+ return trimZeros(text);
680
+ }
681
+ return trimZeros(text.slice(0, marker)) + "e" + text.slice(marker + 1).replace("+", "");
682
+ }
683
+ function trimZeros(text) {
684
+ if (text.indexOf(".") < 0) {
685
+ return text;
686
+ }
687
+ var end = text.length;
688
+ while (end > 0 && text.charAt(end - 1) === "0") {
689
+ end -= 1;
690
+ }
691
+ if (text.charAt(end - 1) === ".") {
692
+ end -= 1;
693
+ }
694
+ return text.slice(0, end);
695
+ }`;
696
+ /**
697
+ * The page-side script that builds one grid for each table block.
698
+ *
699
+ * The script walks the grid mounts. Each mount names its block, and the registry holds the decoded rows and
700
+ * the column display of that block. Thus the boot reads what the server resolved, and it resolves nothing
701
+ * again. A mount whose block the registry does not hold keeps its empty card, and the walk continues.
702
+ *
703
+ * The column definition takes the label, the filter, and the two readings of a cell. The formatter gives the
704
+ * shown text under the kind of the column, and the tooltip gives the raw cell where the shown text differs
705
+ * from it. A value getter reads the own key of the row, because the field of a column reads a point as a
706
+ * path and a column name can hold one. A number column also parses the cell for its filter, thus a column
707
+ * of numeric text compares as a number and a sentinel matches nothing.
708
+ *
709
+ * The row model is the client-side model. It renders the visible slice alone, thus a table of many thousands
710
+ * of rows costs the DOM a screen of rows. The mount takes the height of its rows, up to the visible count,
711
+ * thus a short table leaves no empty box under it. A wide table adds the horizontal scroll bar that the
712
+ * host paints, measured at the first data render, thus the bar covers no row.
713
+ *
714
+ * The status of the card reads the model of the grid. The grid gives a model event after each filter and
715
+ * each sort, thus the count states what the reader sees against what the table holds.
716
+ *
717
+ * The print hooks switch the grid to its print layout. That layout lays every row out at once and it holds
718
+ * no scroll viewport, thus each row of the print form reaches the paper. The print stops at the print bound,
719
+ * and the note of the card then states what the paper shows and where the whole table is.
720
+ *
721
+ * One grid builds inside a guard. A malformed payload is exactly the fault that a look must diagnose, thus
722
+ * it must never stop a sibling grid and it must never throw out of the page.
723
+ */
724
+ export const GRID_BOOTSTRAP = `(function () {
725
+ ${TABLE_CELL_FORMATTER}
726
+ var registry = window.${TABLE_DATA_GLOBAL};
727
+ var mounts = document.querySelectorAll("[${GRID_MOUNT_ATTRIBUTE}]");
728
+ if (!registry || typeof agGrid === "undefined" || mounts.length === 0) {
729
+ return;
730
+ }
731
+ if (agGrid.ModuleRegistry && agGrid.AllCommunityModule) {
732
+ agGrid.ModuleRegistry.registerModules([agGrid.AllCommunityModule]);
733
+ }
734
+ var theme = agGrid.themeQuartz.withParams(${scriptJson(GRID_THEME_PARAMS)});
735
+ function columnOf(name, display) {
736
+ var kind = display.kind;
737
+ var bound = display.bound;
738
+ var label = display.label || name;
739
+ var column = {
740
+ colId: name,
741
+ headerName: label,
742
+ valueGetter: function (params) {
743
+ return params.data ? params.data[name] : undefined;
744
+ },
745
+ valueFormatter: function (params) {
746
+ return params.value === undefined || params.value === null ? "" : formatCell(params.value, kind, bound);
747
+ },
748
+ tooltipValueGetter: function (params) {
749
+ if (params.value === undefined || params.value === null) {
750
+ return "";
751
+ }
752
+ var raw = String(params.value);
753
+ return raw === formatCell(params.value, kind, bound) ? "" : raw;
754
+ },
755
+ filter: display.filter === "number" ? "agNumberColumnFilter" : "agTextColumnFilter"
756
+ };
757
+ if (display.filter === "number") {
758
+ // The number filter compares numbers. A cell of numeric text would compare as text under it, thus the
759
+ // filter reads the parsed value and a cell that parses to nothing matches no comparison.
760
+ column.filterValueGetter = function (params) {
761
+ var cell = params.data ? params.data[name] : undefined;
762
+ var parsed = cell === undefined || cell === null || cell === "" ? NaN : Number(cell);
763
+ return isFinite(parsed) ? parsed : null;
764
+ };
765
+ }
766
+ if (label !== name) {
767
+ column.headerTooltip = name;
768
+ }
769
+ return column;
770
+ }
771
+ function scrollBarHeight(mount) {
772
+ // The grid lays its horizontal bar in a strip of its own under the rows, and that strip takes its space
773
+ // from the box of the mount. Thus the height of the strip is what the last row loses, and an overlay bar
774
+ // leaves the strip at zero. A grid that names the strip differently gives none, and the box then holds
775
+ // the rows alone.
776
+ var strip = mount.querySelector(".ag-body-horizontal-scroll");
777
+ return strip ? strip.offsetHeight : 0;
778
+ }
779
+ function fitToScrollBar(mount, rowSpace) {
780
+ var height = rowSpace + scrollBarHeight(mount) + "px";
781
+ if (mount.style.height !== height) {
782
+ mount.style.height = height;
783
+ }
784
+ }
785
+ function onFirstRender(mount, rowSpace) {
786
+ // The grid lays its columns out with the first data render. A measure before that render finds no
787
+ // viewport and no bar, thus the fit waits for the grid to say that the rows are on the page.
788
+ return function () {
789
+ fitToScrollBar(mount, rowSpace);
790
+ };
791
+ }
792
+ function statusText(shown, total) {
793
+ var whole = formatCell(total, "compact-scientific") + " ${GRID_ROWS_WORD}";
794
+ return shown === total ? whole : formatCell(shown, "compact-scientific") + " of " + whole;
795
+ }
796
+ function onModelUpdate(count, total) {
797
+ // The grid gives this event after each filter and each sort, thus the status states what the reader
798
+ // sees against what the table holds.
799
+ return function (params) {
800
+ if (count) {
801
+ count.textContent = statusText(params.api.getDisplayedRowCount(), total);
802
+ }
803
+ };
804
+ }
805
+ function printNote(total) {
806
+ return (
807
+ "The print shows the first " +
808
+ formatCell(${GRID_PRINT_ROW_CAP}, "compact-scientific") +
809
+ " of " +
810
+ formatCell(total, "compact-scientific") +
811
+ " ${GRID_ROWS_WORD}. The full table rides the download."
812
+ );
813
+ }
814
+ function bindPrint(api, mount, note, rows) {
815
+ var capped = rows.length > ${GRID_PRINT_ROW_CAP};
816
+ var height = "";
817
+ window.addEventListener("beforeprint", function () {
818
+ // The height of the screen form is whatever the fit left, thus the restore reads it at print time.
819
+ height = mount.style.height;
820
+ mount.style.height = "auto";
821
+ if (capped) {
822
+ api.setGridOption("rowData", rows.slice(0, ${GRID_PRINT_ROW_CAP}));
823
+ if (note) {
824
+ note.textContent = printNote(rows.length);
825
+ }
826
+ }
827
+ api.setGridOption("domLayout", "print");
828
+ });
829
+ window.addEventListener("afterprint", function () {
830
+ api.setGridOption("domLayout", "normal");
831
+ if (capped) {
832
+ api.setGridOption("rowData", rows);
833
+ if (note) {
834
+ note.textContent = "";
835
+ }
836
+ }
837
+ mount.style.height = height;
838
+ });
839
+ }
840
+ for (var i = 0; i < mounts.length; i++) {
841
+ var mount = mounts[i];
842
+ var id = mount.getAttribute("${GRID_MOUNT_ATTRIBUTE}") || "";
843
+ var payload = Object.prototype.hasOwnProperty.call(registry, id) ? registry[id] : null;
844
+ if (!payload || !payload.columns || !payload.display) {
845
+ continue;
846
+ }
847
+ var card = mount.parentNode;
848
+ var note = card ? card.querySelector(".${GRID_NOTE_CLASS}") : null;
849
+ var count = card ? card.querySelector(".${GRID_COUNT_CLASS}") : null;
850
+ try {
851
+ var columns = [];
852
+ for (var c = 0; c < payload.columns.length; c++) {
853
+ columns.push(columnOf(payload.columns[c], payload.display[c] || {}));
854
+ }
855
+ var rows = payload.rows || [];
856
+ // The total is the row count of the artifact before a row bound cut it. A payload that carries none
857
+ // states the whole artifact already, thus the row count answers.
858
+ var total = typeof payload.total === "number" ? payload.total : rows.length;
859
+ var shown = rows.length < ${GRID_VISIBLE_ROWS} ? rows.length : ${GRID_VISIBLE_ROWS};
860
+ var rowSpace = ${GRID_HEADER_HEIGHT_PX + GRID_HEADER_BORDER_PX} + shown * ${GRID_ROW_HEIGHT_PX};
861
+ mount.style.height = rowSpace + "px";
862
+ var api = agGrid.createGrid(mount, {
863
+ theme: theme,
864
+ columnDefs: columns,
865
+ rowData: rows,
866
+ defaultColDef: { sortable: true, resizable: true, flex: 1, minWidth: ${GRID_MIN_COLUMN_WIDTH_PX} },
867
+ suppressCellFocus: true,
868
+ // The tooltip carries the raw value of a shown cell. A reader hovers to read that value, thus the
869
+ // delay is short and the value arrives while the pointer is still on the cell.
870
+ tooltipShowDelay: ${GRID_TOOLTIP_DELAY_MS},
871
+ onFirstDataRendered: onFirstRender(mount, rowSpace),
872
+ onModelUpdated: onModelUpdate(count, total)
873
+ });
874
+ bindPrint(api, mount, note, rows);
875
+ } catch (cause) {
876
+ console.error("grid boot failed for " + id + ": " + (cause && cause.message ? cause.message : cause));
877
+ }
878
+ }
879
+ })();`;