@inflexa-ai/harness 0.20.0 → 0.21.1

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 (133) 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 +35 -9
  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/loop/run-agent.js +58 -23
  27. package/dist/loop/tool-detail.d.ts +19 -2
  28. package/dist/loop/tool-detail.js +35 -2
  29. package/dist/memory/content-to-cortex.d.ts +3 -2
  30. package/dist/memory/content-to-cortex.js +21 -12
  31. package/dist/memory/conversation-display-replay.js +14 -7
  32. package/dist/memory/thread-history.d.ts +60 -3
  33. package/dist/memory/thread-history.js +55 -16
  34. package/dist/memory/thread-store.d.ts +11 -1
  35. package/dist/memory/thread-store.js +12 -2
  36. package/dist/prompts/conversation.d.ts +1 -1
  37. package/dist/prompts/conversation.js +23 -6
  38. package/dist/prompts/report-session.d.ts +1 -1
  39. package/dist/prompts/report-session.js +190 -9
  40. package/dist/report-model/authoring-grammar.d.ts +463 -0
  41. package/dist/report-model/authoring-grammar.js +77 -0
  42. package/dist/report-model/block-walk.d.ts +57 -14
  43. package/dist/report-model/block-walk.js +116 -11
  44. package/dist/report-model/draft-finish.d.ts +22 -4
  45. package/dist/report-model/draft-finish.js +39 -7
  46. package/dist/report-model/draft-operations.d.ts +3 -0
  47. package/dist/report-model/draft-operations.js +19 -5
  48. package/dist/report-model/draft-read.d.ts +8 -5
  49. package/dist/report-model/draft-read.js +21 -5
  50. package/dist/report-model/fixture-resolver.js +15 -4
  51. package/dist/report-model/pin-snapshot.d.ts +24 -6
  52. package/dist/report-model/pin-snapshot.js +204 -6
  53. package/dist/report-model/production-resolver.js +15 -4
  54. package/dist/report-model/reference-resolver.d.ts +50 -1
  55. package/dist/report-model/reference-resolver.js +94 -1
  56. package/dist/report-model/reference-stamp.d.ts +45 -0
  57. package/dist/report-model/reference-stamp.js +173 -0
  58. package/dist/report-model/structural-validation.d.ts +4 -1
  59. package/dist/report-model/structural-validation.js +48 -3
  60. package/dist/report-model/validate.d.ts +1 -1
  61. package/dist/report-model/validate.js +1 -1
  62. package/dist/report-render/asset-lookup.d.ts +19 -0
  63. package/dist/report-render/asset-lookup.js +37 -0
  64. package/dist/report-render/assets.d.ts +45 -1
  65. package/dist/report-render/assets.js +65 -6
  66. package/dist/report-render/chart-presets.d.ts +102 -0
  67. package/dist/report-render/chart-presets.js +205 -0
  68. package/dist/report-render/chart.d.ts +112 -10
  69. package/dist/report-render/chart.js +1164 -41
  70. package/dist/report-render/design.d.ts +99 -0
  71. package/dist/report-render/design.js +289 -107
  72. package/dist/report-render/number-format.d.ts +117 -0
  73. package/dist/report-render/number-format.js +479 -0
  74. package/dist/report-render/page.d.ts +121 -5
  75. package/dist/report-render/page.js +689 -7
  76. package/dist/report-render/references.d.ts +70 -16
  77. package/dist/report-render/references.js +54 -22
  78. package/dist/report-render/render.d.ts +23 -5
  79. package/dist/report-render/render.js +81 -22
  80. package/dist/report-render/table-data.d.ts +92 -0
  81. package/dist/report-render/table-data.js +129 -0
  82. package/dist/report-render/types.d.ts +15 -0
  83. package/dist/report-render/value-bridge.js +6 -2
  84. package/dist/report-render/views/chart-view.d.ts +12 -5
  85. package/dist/report-render/views/chart-view.js +14 -8
  86. package/dist/report-render/views/page-view.d.ts +36 -15
  87. package/dist/report-render/views/page-view.js +45 -23
  88. package/dist/report-render/views/prose.d.ts +12 -2
  89. package/dist/report-render/views/prose.js +29 -7
  90. package/dist/report-render/views/references-view.d.ts +22 -8
  91. package/dist/report-render/views/references-view.js +101 -13
  92. package/dist/report-render/views/values.d.ts +128 -13
  93. package/dist/report-render/views/values.js +199 -18
  94. package/dist/runtime/assemble.d.ts +39 -5
  95. package/dist/runtime/assemble.js +47 -6
  96. package/dist/sandbox/client.d.ts +10 -1
  97. package/dist/sandbox/create-sandbox.d.ts +9 -4
  98. package/dist/sandbox/create-sandbox.js +22 -16
  99. package/dist/sandbox/docker-client.js +10 -7
  100. package/dist/sandbox/mount-plan.d.ts +45 -7
  101. package/dist/sandbox/mount-plan.js +43 -5
  102. package/dist/sandbox/types.d.ts +6 -0
  103. package/dist/state/init.js +38 -9
  104. package/dist/state/report-session-state.d.ts +42 -1
  105. package/dist/state/report-session-state.js +70 -2
  106. package/dist/state/report-versions.d.ts +22 -17
  107. package/dist/state/report-versions.js +48 -35
  108. package/dist/state/snapshot-parse.js +16 -1
  109. package/dist/tools/define-tool.d.ts +32 -0
  110. package/dist/tools/define-tool.js +4 -0
  111. package/dist/tools/detail-resolver.d.ts +7 -2
  112. package/dist/tools/detail-resolver.js +7 -2
  113. package/dist/tools/iterate-report.d.ts +1 -1
  114. package/dist/tools/report/preview-snapshot.js +1 -0
  115. package/dist/tools/report-authoring/authoring-tools.d.ts +15 -6
  116. package/dist/tools/report-authoring/authoring-tools.js +99 -18
  117. package/dist/tools/report-session/derive-table.d.ts +162 -0
  118. package/dist/tools/report-session/derive-table.js +459 -0
  119. package/dist/tools/report-session/examine-page.d.ts +48 -18
  120. package/dist/tools/report-session/examine-page.js +204 -24
  121. package/dist/tools/report-session/index.d.ts +2 -0
  122. package/dist/tools/report-session/index.js +2 -0
  123. package/dist/tools/report-session/list-artifacts.d.ts +97 -0
  124. package/dist/tools/report-session/list-artifacts.js +197 -0
  125. package/dist/tools/report-session/preview-report.d.ts +6 -0
  126. package/dist/tools/report-session/preview-report.js +180 -28
  127. package/dist/tools/report-session/record-version.d.ts +22 -10
  128. package/dist/tools/report-session/record-version.js +99 -14
  129. package/dist/tools/start-report-session.d.ts +115 -0
  130. package/dist/tools/start-report-session.js +179 -0
  131. package/dist/workspace/paths.d.ts +50 -0
  132. package/dist/workspace/paths.js +63 -0
  133. package/package.json +2 -1
@@ -0,0 +1,173 @@
1
+ /**
2
+ * The hash stamp of the land path, and the hash elision of the read.
3
+ *
4
+ * An artifact reference pins a path and a content hash. The pinned snapshot already maps each path onto
5
+ * its hash, thus the author of a block gives the path alone. The stamp fills the hash from the snapshot,
6
+ * and a reference that names the path lands.
7
+ *
8
+ * The stamp also drops a hash that a payload carries. The snapshot owns the value. Thus an echoed binding
9
+ * and a mistyped one both land with the hash of the snapshot entry, and the structural tier reads no stale
10
+ * pin on the land path.
11
+ *
12
+ * The walk is structural, because it runs before the grammar parse. It reads the artifact-reference shape
13
+ * alone: a pinned kind and a string path. Every other value passes through untouched, thus a nested block,
14
+ * a derivation input, and a plain field are all safe.
15
+ *
16
+ * A reference whose path the snapshot does not hold refuses here. Without this arm the parse refuses the
17
+ * absent hash, and that message sends the author to the wrong repair.
18
+ *
19
+ * The elision walks the same shape in the other direction: a read of a landed block gives each binding
20
+ * with no hash. Thus one module holds both directions, and the two agree on which object carries a pin.
21
+ */
22
+ import { err, ok } from "neverthrow";
23
+ import { snapshotEntry } from "./reference-resolver.js";
24
+ /**
25
+ * The three reference kinds that name one artifact directly, thus the three kinds that take a hash.
26
+ *
27
+ * The `satisfies` clause ties each literal to a member of the reference union. A renamed kind becomes a
28
+ * compile error here. A new kind passes with no error, because the stamp fills a known kind and it never
29
+ * refuses one.
30
+ */
31
+ const PINNED_KINDS = new Set(["artifact-value", "artifact-table", "artifact-file"]);
32
+ /** Give `true` when an object carries an artifact pin. Such an object takes a stamp, and a read removes its hash. */
33
+ function holdsPin(record) {
34
+ const { kind } = record;
35
+ return typeof kind === "string" && PINNED_KINDS.has(kind);
36
+ }
37
+ /**
38
+ * Put one own data property on a copy.
39
+ *
40
+ * A plain assignment at the key `__proto__` reaches the prototype setter, thus the copy loses an own key
41
+ * that the payload carries. The definition makes an own data property for each key.
42
+ */
43
+ function defineOwn(target, key, value) {
44
+ Object.defineProperty(target, key, { value, enumerable: true, writable: true, configurable: true });
45
+ }
46
+ /**
47
+ * Give the artifact path of an object that takes a stamp, or `undefined` when the object takes none.
48
+ *
49
+ * The test reads the shape alone, because the payload reaches the stamp before the grammar parse. A hash
50
+ * that the object carries decides nothing here, because the stamp writes the hash of the snapshot over it.
51
+ */
52
+ function stampablePath(record) {
53
+ const { path } = record;
54
+ if (!holdsPin(record) || typeof path !== "string") {
55
+ return undefined;
56
+ }
57
+ return path;
58
+ }
59
+ /**
60
+ * Walk one value, and give the value with the hash of each artifact reference filled from the snapshot.
61
+ *
62
+ * The walk builds a copy on the changed path only. A branch that no stamp touched comes back as the same
63
+ * reference, thus the input stays intact and the copy costs the size of the change.
64
+ */
65
+ function stampValue(value, snapshot, missing) {
66
+ if (Array.isArray(value)) {
67
+ let changed = false;
68
+ const next = [];
69
+ for (const item of value) {
70
+ const stamped = stampValue(item, snapshot, missing);
71
+ if (stamped !== item) {
72
+ changed = true;
73
+ }
74
+ next.push(stamped);
75
+ }
76
+ return changed ? next : value;
77
+ }
78
+ if (value === null || typeof value !== "object") {
79
+ return value;
80
+ }
81
+ const record = value;
82
+ let changed = false;
83
+ const next = {};
84
+ for (const key of Object.keys(record)) {
85
+ const stamped = stampValue(record[key], snapshot, missing);
86
+ if (stamped !== record[key]) {
87
+ changed = true;
88
+ }
89
+ defineOwn(next, key, stamped);
90
+ }
91
+ const path = stampablePath(record);
92
+ if (path === undefined) {
93
+ return changed ? next : value;
94
+ }
95
+ const entry = snapshotEntry(snapshot, path);
96
+ if (entry === undefined) {
97
+ missing.push({ path, record });
98
+ return changed ? next : value;
99
+ }
100
+ return { ...next, hash: entry.hash };
101
+ }
102
+ /**
103
+ * Stamp the hash of each artifact reference in a block payload, from the snapshot entry at its path.
104
+ *
105
+ * The membership read is `snapshotEntry`, because the path is author text. An own key alone counts, thus
106
+ * an inherited member of a plain object reads as absent and the stamp agrees with the structural tier.
107
+ *
108
+ * The whole call refuses when one reference names a path that the snapshot does not hold. A partial stamp
109
+ * beside a refusal would land nothing, thus the refusal carries every unknown path at one time and the
110
+ * author repairs them together.
111
+ */
112
+ export function stampReferenceHashes(payload, snapshot) {
113
+ const missing = [];
114
+ const stamped = stampValue(payload, snapshot, missing);
115
+ if (missing.length === 0) {
116
+ return ok(stamped);
117
+ }
118
+ const unresolved = missing.map((pin) => ({
119
+ // The payload reaches the stamp before the parse, thus the object holds the fields of a reference
120
+ // as the author wrote them. The refusal names that object, and this assertion is the one place
121
+ // where the pre-parse shape enters the parsed reference type.
122
+ reference: pin.record,
123
+ reason: "artifact-missing",
124
+ detail: `no artifact at ${pin.path}`,
125
+ }));
126
+ return err({
127
+ reason: "unresolved-reference",
128
+ detail: `the pinned evidence holds no artifact at ${missing.map((pin) => pin.path).join(", ")}`,
129
+ unresolved,
130
+ });
131
+ }
132
+ /**
133
+ * Give one value with the stamped hash of each artifact reference removed.
134
+ *
135
+ * The snapshot owns the hash, thus a read that echoed one would give an agent a value to mistype. A binding
136
+ * still gives its path and its locator, which are what an agent authored and what it changes.
137
+ *
138
+ * The walk builds a copy on the changed path only. A branch that carries no pin comes back as the same
139
+ * reference. Thus the read costs the size of the pins, and never the size of the block.
140
+ */
141
+ export function stripReferenceHashes(value) {
142
+ if (Array.isArray(value)) {
143
+ let changed = false;
144
+ const next = [];
145
+ for (const item of value) {
146
+ const stripped = stripReferenceHashes(item);
147
+ if (stripped !== item) {
148
+ changed = true;
149
+ }
150
+ next.push(stripped);
151
+ }
152
+ return changed ? next : value;
153
+ }
154
+ if (value === null || typeof value !== "object") {
155
+ return value;
156
+ }
157
+ const record = value;
158
+ const pinned = holdsPin(record);
159
+ let changed = false;
160
+ const next = {};
161
+ for (const key of Object.keys(record)) {
162
+ if (pinned && key === "hash") {
163
+ changed = true;
164
+ continue;
165
+ }
166
+ const stripped = stripReferenceHashes(record[key]);
167
+ if (stripped !== record[key]) {
168
+ changed = true;
169
+ }
170
+ defineOwn(next, key, stripped);
171
+ }
172
+ return changed ? next : value;
173
+ }
@@ -17,6 +17,9 @@ import { type ReportSnapshot } from "./reference-resolver.js";
17
17
  * value comes from a read of the artifact. An `assert` is the authored belief about that value, thus this
18
18
  * tier matches no assertion and a reference that carries one passes on its pin alone.
19
19
  *
20
+ * `columns` carries each column that the grammar of a chart block names. The walk collects them, thus a
21
+ * chart that plots an invented column refuses before it lands, wherever the snapshot can answer.
22
+ *
20
23
  * The answer is synchronous, because the snapshot is already in memory.
21
24
  */
22
- export declare function validateReferenceStructure(reference: Reference, snapshot: ReportSnapshot): Result<void, UnresolvedReference>;
25
+ export declare function validateReferenceStructure(reference: Reference, snapshot: ReportSnapshot, columns?: readonly string[]): Result<void, UnresolvedReference>;
@@ -8,7 +8,7 @@
8
8
  * value. As a result the costly read happens one time for each report version, and never for each edit.
9
9
  */
10
10
  import { err, ok } from "neverthrow";
11
- import { fileTypeHoldsNoCell, snapshotEntry } from "./reference-resolver.js";
11
+ import { columnsHeldByNoRow, fileTypeHoldsNoCell, snapshotEntry } from "./reference-resolver.js";
12
12
  /** Build an `Err` that carries the unresolved reference. The `detail` key is present only when there is a detail to carry. */
13
13
  function fail(reference, reason, detail) {
14
14
  const failure = detail !== undefined ? { reference, reason, detail } : { reference, reason };
@@ -33,6 +33,30 @@ function validatePin(reference, snapshot) {
33
33
  }
34
34
  return ok();
35
35
  }
36
+ /**
37
+ * Match each column that a chart grammar names against the table that the block binds.
38
+ *
39
+ * The tier reads the snapshot alone, thus it answers from the two things that the snapshot holds. A table
40
+ * reference that declares a column subset answers on its own terms, because a name outside the subset
41
+ * addresses nothing. A snapshot entry that carries rows answers from those rows.
42
+ *
43
+ * A snapshot that pins identity alone holds no rows. Such a snapshot contradicts no name, thus the tier
44
+ * passes and the value tier settles the match over the artifact that it reads.
45
+ */
46
+ function validateColumns(reference, snapshot, columns) {
47
+ const subset = reference.columns;
48
+ if (subset !== undefined) {
49
+ const outside = columns.filter((column) => !subset.includes(column));
50
+ if (outside.length > 0) {
51
+ return fail(reference, "locator-out-of-range", `the chart names column ${outside.join(", ")}, which the bound column subset leaves out`);
52
+ }
53
+ }
54
+ const absent = columnsHeldByNoRow(snapshotEntry(snapshot, reference.path)?.rows ?? [], columns);
55
+ if (absent.length > 0) {
56
+ return fail(reference, "locator-out-of-range", `the chart names column ${absent.join(", ")}, which the bound table does not hold`);
57
+ }
58
+ return ok();
59
+ }
36
60
  /**
37
61
  * Validate one reference against the snapshot.
38
62
  *
@@ -40,12 +64,33 @@ function validatePin(reference, snapshot) {
40
64
  * value comes from a read of the artifact. An `assert` is the authored belief about that value, thus this
41
65
  * tier matches no assertion and a reference that carries one passes on its pin alone.
42
66
  *
67
+ * `columns` carries each column that the grammar of a chart block names. The walk collects them, thus a
68
+ * chart that plots an invented column refuses before it lands, wherever the snapshot can answer.
69
+ *
43
70
  * The answer is synchronous, because the snapshot is already in memory.
44
71
  */
45
- export function validateReferenceStructure(reference, snapshot) {
72
+ export function validateReferenceStructure(reference, snapshot, columns) {
46
73
  switch (reference.kind) {
74
+ case "artifact-table": {
75
+ const pin = validatePin(reference, snapshot);
76
+ if (pin.isErr()) {
77
+ return pin;
78
+ }
79
+ // The bound is content: it decides which rows the table holds. Thus a bound column that the
80
+ // artifact does not hold refuses here, exactly as a grammar column of a chart does.
81
+ const bound = reference.rowBound;
82
+ if (bound !== undefined) {
83
+ const absent = columnsHeldByNoRow(snapshotEntry(snapshot, reference.path)?.rows ?? [], [bound.column]);
84
+ if (absent.length > 0) {
85
+ return fail(reference, "locator-out-of-range", `the row bound names column ${bound.column}, which the bound table does not hold`);
86
+ }
87
+ }
88
+ if (columns === undefined || columns.length === 0) {
89
+ return ok();
90
+ }
91
+ return validateColumns(reference, snapshot, columns);
92
+ }
47
93
  case "artifact-value":
48
- case "artifact-table":
49
94
  case "artifact-file":
50
95
  return validatePin(reference, snapshot);
51
96
  case "derivation": {
@@ -5,7 +5,7 @@
5
5
  * by construction, and a metric value slot admits one scalar reference only. Thus a schema parse failure
6
6
  * is the grammar rejection, and the validator does not re-implement it. The validator adds the three
7
7
  * checks that a schema cannot make: it makes sure that each block id occurs one time only, it resolves
8
- * each reference against the pinned evidence, and it warns about a free numeral in prose.
8
+ * each reference against the pinned evidence, and it warns about the prose of each block.
9
9
  */
10
10
  import type { UnresolvedReference } from "../contracts/report-reference.js";
11
11
  import { type AnyBlock, type CollectedReference, type ReportWarning } from "./block-walk.js";
@@ -5,7 +5,7 @@
5
5
  * by construction, and a metric value slot admits one scalar reference only. Thus a schema parse failure
6
6
  * is the grammar rejection, and the validator does not re-implement it. The validator adds the three
7
7
  * checks that a schema cannot make: it makes sure that each block id occurs one time only, it resolves
8
- * each reference against the pinned evidence, and it warns about a free numeral in prose.
8
+ * each reference against the pinned evidence, and it warns about the prose of each block.
9
9
  */
10
10
  import { ReportDocumentSchema } from "../contracts/report-blocks.js";
11
11
  import { allWithConcurrency } from "../lib/async-utils.js";
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The lookup of the source file of one staged page asset.
3
+ *
4
+ * A manifest entry (`assets.ts`) names a module specifier, and a caller that stages the assets beside a page
5
+ * needs the file on disk. The resolution reads the installation of the harness, thus the staged bytes are
6
+ * the bytes of the pinned version.
7
+ *
8
+ * The module sits beside the manifest, thus the preview tool and the fixture script read one lookup and
9
+ * neither one carries the module graph of the other.
10
+ */
11
+ /**
12
+ * The absolute path of the source file of one manifest specifier.
13
+ *
14
+ * A package that publishes an `exports` map refuses a subpath that the map does not name, and a browser
15
+ * bundle beside the module entry is such a subpath. The fallback then reads the specifier as a path under
16
+ * each candidate `node_modules` directory of the resolution. Thus a bundle that the map hides still stages,
17
+ * and a specifier that names no file still fails with the resolution error of the package.
18
+ */
19
+ export declare function resolvePageAssetFromInstallation(specifier: string): string;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * The lookup of the source file of one staged page asset.
3
+ *
4
+ * A manifest entry (`assets.ts`) names a module specifier, and a caller that stages the assets beside a page
5
+ * needs the file on disk. The resolution reads the installation of the harness, thus the staged bytes are
6
+ * the bytes of the pinned version.
7
+ *
8
+ * The module sits beside the manifest, thus the preview tool and the fixture script read one lookup and
9
+ * neither one carries the module graph of the other.
10
+ */
11
+ import { existsSync } from "node:fs";
12
+ import { createRequire } from "node:module";
13
+ import { join } from "node:path";
14
+ /** The resolver of a package source. It reads the installation that holds this module. */
15
+ const moduleResolver = createRequire(import.meta.url);
16
+ /**
17
+ * The absolute path of the source file of one manifest specifier.
18
+ *
19
+ * A package that publishes an `exports` map refuses a subpath that the map does not name, and a browser
20
+ * bundle beside the module entry is such a subpath. The fallback then reads the specifier as a path under
21
+ * each candidate `node_modules` directory of the resolution. Thus a bundle that the map hides still stages,
22
+ * and a specifier that names no file still fails with the resolution error of the package.
23
+ */
24
+ export function resolvePageAssetFromInstallation(specifier) {
25
+ try {
26
+ return moduleResolver.resolve(specifier);
27
+ }
28
+ catch (cause) {
29
+ for (const directory of moduleResolver.resolve.paths(specifier) ?? []) {
30
+ const candidate = join(directory, specifier);
31
+ if (existsSync(candidate)) {
32
+ return candidate;
33
+ }
34
+ }
35
+ throw cause;
36
+ }
37
+ }
@@ -5,13 +5,25 @@
5
5
  * names the staged file and the module specifier of its source. The caller resolves each specifier against
6
6
  * its own installation, and it copies the bytes beside the page.
7
7
  *
8
+ * Each entry of the manifest names a file under `deps/`. Thus the shipped libraries and the shipped fonts
9
+ * sit in one directory of their own, and the files that the report produced keep the root of the assets
10
+ * directory. A reader of the directory then tells the two apart on sight, and the sweep of the stage governs
11
+ * the report-side files alone.
12
+ *
8
13
  * The module holds data alone. The renderer reads no file, thus the page stays a pure function of its
9
14
  * inputs.
10
15
  */
11
16
  /** The directory of the staged assets, relative to the page. */
12
17
  export declare const ASSETS_DIR = "assets";
13
18
  /**
14
- * One staged asset. `file` is the basename under the assets directory, and `specifier` is the module
19
+ * The directory of the shipped libraries and fonts, under the assets directory.
20
+ *
21
+ * The manifest, the skeleton, and the stage each spell the segment through this constant. Thus the tag of
22
+ * the page, the file that the stage writes, and the set that the sweep keeps cannot disagree.
23
+ */
24
+ export declare const DEPS_DIR = "deps";
25
+ /**
26
+ * One staged asset. `file` is the path under the assets directory, and `specifier` is the module
15
27
  * specifier of the source file inside the installed package.
16
28
  */
17
29
  export interface PageAsset {
@@ -20,8 +32,40 @@ export interface PageAsset {
20
32
  }
21
33
  /** The relative source of a staged asset. The page directory is self-contained, thus the source is `assets/<file>`. */
22
34
  export declare function assetSource(asset: PageAsset): string;
35
+ /** The relative source of a staged file by its name. A page reference never spells the directory itself. */
36
+ export declare function stagedSource(file: string): string;
37
+ /**
38
+ * The staged name of the raw bytes of one table artifact: a hash head, then the basename of the path.
39
+ *
40
+ * The reader downloads the pinned file itself and never a re-serialization of it, thus the name must carry
41
+ * the identity of those bytes. The hash head makes two artifacts of one basename two staged files, and the
42
+ * basename keeps the downloaded file recognizable on the disk of the reader.
43
+ *
44
+ * The hash arrives as `algorithm:hex`, and neither the colon nor a path separator is safe in a file name.
45
+ * The head takes the hex alone, and the basename takes each unsafe run as one dash.
46
+ */
47
+ export declare function tableSidecarName(hash: string, path: string): string;
48
+ /**
49
+ * The staged name of the script of one derivation: a hash head, in the content-address style of a data
50
+ * asset.
51
+ *
52
+ * The chain line of the appendix links this name, and the stage writes the script text under it. Both read
53
+ * this one function, thus the link and the file cannot disagree. The head is the head that the chain line
54
+ * shows, thus a reader compares the shown hash against the file name by sight.
55
+ *
56
+ * The extension is `.py`, because a session derivation runs its script through the Python interpreter of the
57
+ * container.
58
+ */
59
+ export declare function derivationScriptName(scriptHash: string): string;
23
60
  /** The chart runtime. The page-side bootstrap reads the `echarts` global that this file declares. */
24
61
  export declare const ECHARTS_ASSET: PageAsset;
62
+ /**
63
+ * The grid runtime. The page-side bootstrap reads the `agGrid` global that this file declares.
64
+ *
65
+ * The entry names the browser bundle of the package. The package `exports` map publishes the module
66
+ * entries alone, thus this specifier resolves as a path under the installed package directory.
67
+ */
68
+ export declare const AG_GRID_ASSET: PageAsset;
25
69
  /** The sans font. The file is the latin subset of the variable font, thus one file carries each weight. */
26
70
  export declare const SANS_FONT_ASSET: PageAsset;
27
71
  /**
@@ -5,23 +5,81 @@
5
5
  * names the staged file and the module specifier of its source. The caller resolves each specifier against
6
6
  * its own installation, and it copies the bytes beside the page.
7
7
  *
8
+ * Each entry of the manifest names a file under `deps/`. Thus the shipped libraries and the shipped fonts
9
+ * sit in one directory of their own, and the files that the report produced keep the root of the assets
10
+ * directory. A reader of the directory then tells the two apart on sight, and the sweep of the stage governs
11
+ * the report-side files alone.
12
+ *
8
13
  * The module holds data alone. The renderer reads no file, thus the page stays a pure function of its
9
14
  * inputs.
10
15
  */
11
16
  /** The directory of the staged assets, relative to the page. */
12
17
  export const ASSETS_DIR = "assets";
18
+ /**
19
+ * The directory of the shipped libraries and fonts, under the assets directory.
20
+ *
21
+ * The manifest, the skeleton, and the stage each spell the segment through this constant. Thus the tag of
22
+ * the page, the file that the stage writes, and the set that the sweep keeps cannot disagree.
23
+ */
24
+ export const DEPS_DIR = "deps";
13
25
  /** The relative source of a staged asset. The page directory is self-contained, thus the source is `assets/<file>`. */
14
26
  export function assetSource(asset) {
15
27
  return `${ASSETS_DIR}/${asset.file}`;
16
28
  }
29
+ /** The relative source of a staged file by its name. A page reference never spells the directory itself. */
30
+ export function stagedSource(file) {
31
+ return `${ASSETS_DIR}/${file}`;
32
+ }
33
+ /** The count of hash characters in a staged name. A staged file carries enough of the hash to be unique. */
34
+ const HASH_CHARS = 12;
35
+ /**
36
+ * The staged name of the raw bytes of one table artifact: a hash head, then the basename of the path.
37
+ *
38
+ * The reader downloads the pinned file itself and never a re-serialization of it, thus the name must carry
39
+ * the identity of those bytes. The hash head makes two artifacts of one basename two staged files, and the
40
+ * basename keeps the downloaded file recognizable on the disk of the reader.
41
+ *
42
+ * The hash arrives as `algorithm:hex`, and neither the colon nor a path separator is safe in a file name.
43
+ * The head takes the hex alone, and the basename takes each unsafe run as one dash.
44
+ */
45
+ export function tableSidecarName(hash, path) {
46
+ const hex = hash.slice(hash.lastIndexOf(":") + 1).replace(/[^a-z0-9]+/gi, "");
47
+ const base = path.slice(path.lastIndexOf("/") + 1).replace(/[^a-z0-9._-]+/gi, "-");
48
+ return `${hex.slice(0, HASH_CHARS)}-${base}`;
49
+ }
50
+ /**
51
+ * The staged name of the script of one derivation: a hash head, in the content-address style of a data
52
+ * asset.
53
+ *
54
+ * The chain line of the appendix links this name, and the stage writes the script text under it. Both read
55
+ * this one function, thus the link and the file cannot disagree. The head is the head that the chain line
56
+ * shows, thus a reader compares the shown hash against the file name by sight.
57
+ *
58
+ * The extension is `.py`, because a session derivation runs its script through the Python interpreter of the
59
+ * container.
60
+ */
61
+ export function derivationScriptName(scriptHash) {
62
+ const hex = scriptHash.slice(scriptHash.lastIndexOf(":") + 1).replace(/[^a-z0-9]+/gi, "");
63
+ return `d-${hex.slice(0, HASH_CHARS)}.py`;
64
+ }
17
65
  /** The chart runtime. The page-side bootstrap reads the `echarts` global that this file declares. */
18
66
  export const ECHARTS_ASSET = {
19
- file: "echarts.min.js",
67
+ file: `${DEPS_DIR}/echarts.min.js`,
20
68
  specifier: "echarts/dist/echarts.min.js",
21
69
  };
70
+ /**
71
+ * The grid runtime. The page-side bootstrap reads the `agGrid` global that this file declares.
72
+ *
73
+ * The entry names the browser bundle of the package. The package `exports` map publishes the module
74
+ * entries alone, thus this specifier resolves as a path under the installed package directory.
75
+ */
76
+ export const AG_GRID_ASSET = {
77
+ file: `${DEPS_DIR}/ag-grid-community.min.js`,
78
+ specifier: "ag-grid-community/dist/ag-grid-community.min.js",
79
+ };
22
80
  /** The sans font. The file is the latin subset of the variable font, thus one file carries each weight. */
23
81
  export const SANS_FONT_ASSET = {
24
- file: "space-grotesk-latin-wght-normal.woff2",
82
+ file: `${DEPS_DIR}/space-grotesk-latin-wght-normal.woff2`,
25
83
  specifier: "@fontsource-variable/space-grotesk/files/space-grotesk-latin-wght-normal.woff2",
26
84
  };
27
85
  /**
@@ -32,27 +90,28 @@ export const SANS_FONT_ASSET = {
32
90
  * and 700 for a metric value.
33
91
  */
34
92
  export const MONO_FONT_400_ASSET = {
35
- file: "ibm-plex-mono-latin-400-normal.woff2",
93
+ file: `${DEPS_DIR}/ibm-plex-mono-latin-400-normal.woff2`,
36
94
  specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-400-normal.woff2",
37
95
  };
38
96
  /** The mono font at weight 500, for a tag and a stat-card label. */
39
97
  export const MONO_FONT_500_ASSET = {
40
- file: "ibm-plex-mono-latin-500-normal.woff2",
98
+ file: `${DEPS_DIR}/ibm-plex-mono-latin-500-normal.woff2`,
41
99
  specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-500-normal.woff2",
42
100
  };
43
101
  /** The mono font at weight 600, for a table header, a section label, and the panel badge. */
44
102
  export const MONO_FONT_600_ASSET = {
45
- file: "ibm-plex-mono-latin-600-normal.woff2",
103
+ file: `${DEPS_DIR}/ibm-plex-mono-latin-600-normal.woff2`,
46
104
  specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-600-normal.woff2",
47
105
  };
48
106
  /** The mono font at weight 700, for a metric value. */
49
107
  export const MONO_FONT_700_ASSET = {
50
- file: "ibm-plex-mono-latin-700-normal.woff2",
108
+ file: `${DEPS_DIR}/ibm-plex-mono-latin-700-normal.woff2`,
51
109
  specifier: "@fontsource/ibm-plex-mono/files/ibm-plex-mono-latin-700-normal.woff2",
52
110
  };
53
111
  /** Each asset that the caller stages beside the page. */
54
112
  export const PAGE_ASSETS = [
55
113
  ECHARTS_ASSET,
114
+ AG_GRID_ASSET,
56
115
  SANS_FONT_ASSET,
57
116
  MONO_FONT_400_ASSET,
58
117
  MONO_FONT_500_ASSET,
@@ -0,0 +1,102 @@
1
+ /**
2
+ * The preset expansion of the chart grammar.
3
+ *
4
+ * A preset is sugar over the composition. `expandPreset` gives the composition of one preset, thus the
5
+ * derivation reads one shape and the two paths cannot drift.
6
+ *
7
+ * Each default of a preset is a declared constant of this module, and a declared threshold pair replaces
8
+ * one. A guide line carries such a value, and a guide is never a plotted value. The expansion estimates
9
+ * nothing: the `km` preset plots the precomputed survival columns as they are, and it fits no curve.
10
+ *
11
+ * A preset can also carry a classification. The classification reads the plotted pair of one row against
12
+ * the same values that the guides carry, thus the color split lands on the drawn lines. It reads one row
13
+ * at a time, and it computes no aggregate.
14
+ *
15
+ * A preset declares its own transform on the channel that it transforms. Thus it reads the column of that
16
+ * channel and it drops an authored transform there, and no value takes the transform two times.
17
+ */
18
+ import { type ChartChannel, type ChartComposition, type ChartEncoding, type ChartThresholds, type ChartType } from "../contracts/report-blocks.js";
19
+ /** The four chart types that expand into a composition. */
20
+ export declare const PRESET_CHART_TYPES: readonly ["volcano", "manhattan", "ma", "km"];
21
+ /** One chart type that expands into a composition. */
22
+ export type PresetChartType = (typeof PRESET_CHART_TYPES)[number];
23
+ /** The default p-value threshold of a volcano. The guide line sits at the transformed value of it. */
24
+ export declare const VOLCANO_P_THRESHOLD = 0.05;
25
+ /** The default effect threshold of a volcano. One guide line sits at each side of zero. */
26
+ export declare const VOLCANO_EFFECT_THRESHOLD = 1;
27
+ /** The genome-wide significance threshold of a Manhattan plot. */
28
+ export declare const MANHATTAN_P_THRESHOLD = 5e-8;
29
+ /** The baseline of an MA plot. A point above it rose, and a point below it fell. */
30
+ export declare const MA_BASELINE = 0;
31
+ /** The semantic axis titles of one preset. A preset whose axis carries no fixed quantity states none. */
32
+ export interface PresetAxisTitles {
33
+ readonly x?: string;
34
+ readonly y?: string;
35
+ }
36
+ /** One category of a preset classification: the name that a legend reads, and whether it states no finding. */
37
+ export interface PresetCategory {
38
+ readonly name: string;
39
+ readonly muted: boolean;
40
+ }
41
+ /**
42
+ * The classification rule as plain data: the kind of the split, and the two cuts that it reads.
43
+ *
44
+ * `categoryOf` is a function, thus it reaches no page. A chart that reads the shared payload builds its
45
+ * series in the browser, and this shape is what such a build takes. The two forms answer alike, and the
46
+ * shared test vector holds them together.
47
+ *
48
+ * `cut` sits on the plotted y value, which is the transformed p-value, and `effect` sits on the plotted x
49
+ * value. The volcano rule gives the place of the category in `categories`: 0 for the down side, 1 for the
50
+ * up side, and 2 for the null category.
51
+ */
52
+ export interface PresetRule {
53
+ readonly kind: "volcano";
54
+ readonly cut: number;
55
+ readonly effect: number;
56
+ }
57
+ /**
58
+ * The per-row classification of one preset.
59
+ *
60
+ * `categoryOf` reads the plotted pair of one row and nothing else. Thus it computes no aggregate, and it
61
+ * compares each point against the same numbers that the guide lines carry. The color split then lands on
62
+ * the drawn lines. A row that gives no pair belongs to no category, and its point drops.
63
+ */
64
+ export interface PresetClassification {
65
+ readonly categories: readonly PresetCategory[];
66
+ readonly categoryOf: (x: number | null, y: number | null) => string | undefined;
67
+ readonly rule: PresetRule;
68
+ }
69
+ /** The expansion of one preset: the composition, and the classification where the preset draws one. */
70
+ export interface PresetExpansion {
71
+ readonly composition: ChartComposition;
72
+ readonly classification?: PresetClassification;
73
+ }
74
+ /** True when the chart type expands into a composition. */
75
+ export declare function isPresetChartType(chartType: ChartType): chartType is PresetChartType;
76
+ /**
77
+ * Expand one preset into a composition.
78
+ *
79
+ * `x` and `y` are the two channels that every preset demands. The caller resolves them from the encoding
80
+ * and refuses an absent one, thus this function is total and it needs no failure channel. `encoding`
81
+ * supplies the optional `group` and `label` channels.
82
+ *
83
+ * `thresholds` carries the pair that the author declared. The volcano is the one preset that reads it, and
84
+ * an absent pair gives the two constants of this module.
85
+ */
86
+ export declare function expandPreset(preset: PresetChartType, x: ChartChannel, y: ChartChannel, encoding: ChartEncoding, thresholds?: ChartThresholds): PresetExpansion;
87
+ /**
88
+ * The semantic axis titles that one preset states.
89
+ *
90
+ * A preset knows its own quantities, thus it names them in words. A volcano plots a log2 effect against the
91
+ * transformed p, and a manhattan plots a genome position against the same transformed p. The `ma` and the
92
+ * `km` presets name nothing, because neither axis of them carries a fixed quantity.
93
+ *
94
+ * A channel that carries an authored transform plots a derived quantity, thus the semantic title would
95
+ * state the wrong one. Such a channel takes no title, and the derived name of the renderer states the true
96
+ * quantity. The y channel of a volcano and of a manhattan takes the transform of the preset itself, thus
97
+ * its title is exact.
98
+ *
99
+ * The titles rank under a declared column label. Thus they ride beside the composition and never inside its
100
+ * `axes` field, where an agent title sits and where they would outrank the label.
101
+ */
102
+ export declare function presetAxisTitles(preset: PresetChartType, x: ChartChannel): PresetAxisTitles;