@smthrs/evals 0.0.0-stage → 1.0.0-rc.3

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 (143) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/LICENSE +21 -0
  3. package/README.md +62 -2
  4. package/dist/cjs/Baseline.d.ts +118 -0
  5. package/dist/cjs/Baseline.d.ts.map +1 -0
  6. package/dist/cjs/Baseline.js +158 -0
  7. package/dist/cjs/Baseline.js.map +7 -0
  8. package/dist/cjs/CaseExecutor.d.ts +85 -0
  9. package/dist/cjs/CaseExecutor.d.ts.map +1 -0
  10. package/dist/cjs/CaseExecutor.js +59 -0
  11. package/dist/cjs/CaseExecutor.js.map +7 -0
  12. package/dist/cjs/EvalError.d.ts +64 -0
  13. package/dist/cjs/EvalError.d.ts.map +1 -0
  14. package/dist/cjs/EvalError.js +54 -0
  15. package/dist/cjs/EvalError.js.map +7 -0
  16. package/dist/cjs/Gate.d.ts +55 -0
  17. package/dist/cjs/Gate.d.ts.map +1 -0
  18. package/dist/cjs/Gate.js +88 -0
  19. package/dist/cjs/Gate.js.map +7 -0
  20. package/dist/cjs/Regression.d.ts +116 -0
  21. package/dist/cjs/Regression.d.ts.map +1 -0
  22. package/dist/cjs/Regression.js +191 -0
  23. package/dist/cjs/Regression.js.map +7 -0
  24. package/dist/cjs/Report.d.ts +114 -0
  25. package/dist/cjs/Report.d.ts.map +1 -0
  26. package/dist/cjs/Report.js +161 -0
  27. package/dist/cjs/Report.js.map +7 -0
  28. package/dist/cjs/Runner.d.ts +229 -0
  29. package/dist/cjs/Runner.d.ts.map +1 -0
  30. package/dist/cjs/Runner.js +430 -0
  31. package/dist/cjs/Runner.js.map +7 -0
  32. package/dist/cjs/Suite.d.ts +139 -0
  33. package/dist/cjs/Suite.d.ts.map +1 -0
  34. package/dist/cjs/Suite.js +221 -0
  35. package/dist/cjs/Suite.js.map +7 -0
  36. package/dist/cjs/Trials.d.ts +93 -0
  37. package/dist/cjs/Trials.d.ts.map +1 -0
  38. package/dist/cjs/Trials.js +82 -0
  39. package/dist/cjs/Trials.js.map +7 -0
  40. package/dist/cjs/index.d.ts +76 -0
  41. package/dist/cjs/index.d.ts.map +1 -0
  42. package/dist/cjs/index.js +51 -0
  43. package/dist/cjs/index.js.map +7 -0
  44. package/dist/cjs/internal/CanonicalJson.d.ts +44 -0
  45. package/dist/cjs/internal/CanonicalJson.d.ts.map +1 -0
  46. package/dist/cjs/internal/CanonicalJson.js +114 -0
  47. package/dist/cjs/internal/CanonicalJson.js.map +7 -0
  48. package/dist/cjs/internal/controlCharacters.d.ts +44 -0
  49. package/dist/cjs/internal/controlCharacters.d.ts.map +1 -0
  50. package/dist/cjs/internal/controlCharacters.js +42 -0
  51. package/dist/cjs/internal/controlCharacters.js.map +7 -0
  52. package/dist/cjs/internal/scorerLabel.d.ts +33 -0
  53. package/dist/cjs/internal/scorerLabel.d.ts.map +1 -0
  54. package/dist/cjs/internal/scorerLabel.js +28 -0
  55. package/dist/cjs/internal/scorerLabel.js.map +7 -0
  56. package/dist/cjs/internal/tupleKey.d.ts +20 -0
  57. package/dist/cjs/internal/tupleKey.d.ts.map +1 -0
  58. package/dist/cjs/internal/tupleKey.js +25 -0
  59. package/dist/cjs/internal/tupleKey.js.map +7 -0
  60. package/dist/cjs/package.json +1 -0
  61. package/dist/esm/Baseline.d.ts +118 -0
  62. package/dist/esm/Baseline.d.ts.map +1 -0
  63. package/dist/esm/Baseline.js +203 -0
  64. package/dist/esm/Baseline.js.map +1 -0
  65. package/dist/esm/CaseExecutor.d.ts +85 -0
  66. package/dist/esm/CaseExecutor.d.ts.map +1 -0
  67. package/dist/esm/CaseExecutor.js +60 -0
  68. package/dist/esm/CaseExecutor.js.map +1 -0
  69. package/dist/esm/EvalError.d.ts +64 -0
  70. package/dist/esm/EvalError.d.ts.map +1 -0
  71. package/dist/esm/EvalError.js +65 -0
  72. package/dist/esm/EvalError.js.map +1 -0
  73. package/dist/esm/Gate.d.ts +55 -0
  74. package/dist/esm/Gate.d.ts.map +1 -0
  75. package/dist/esm/Gate.js +90 -0
  76. package/dist/esm/Gate.js.map +1 -0
  77. package/dist/esm/Regression.d.ts +116 -0
  78. package/dist/esm/Regression.d.ts.map +1 -0
  79. package/dist/esm/Regression.js +195 -0
  80. package/dist/esm/Regression.js.map +1 -0
  81. package/dist/esm/Report.d.ts +114 -0
  82. package/dist/esm/Report.d.ts.map +1 -0
  83. package/dist/esm/Report.js +188 -0
  84. package/dist/esm/Report.js.map +1 -0
  85. package/dist/esm/Runner.d.ts +229 -0
  86. package/dist/esm/Runner.d.ts.map +1 -0
  87. package/dist/esm/Runner.js +430 -0
  88. package/dist/esm/Runner.js.map +1 -0
  89. package/dist/esm/Suite.d.ts +139 -0
  90. package/dist/esm/Suite.d.ts.map +1 -0
  91. package/dist/esm/Suite.js +255 -0
  92. package/dist/esm/Suite.js.map +1 -0
  93. package/dist/esm/Trials.d.ts +93 -0
  94. package/dist/esm/Trials.d.ts.map +1 -0
  95. package/dist/esm/Trials.js +116 -0
  96. package/dist/esm/Trials.js.map +1 -0
  97. package/dist/esm/index.d.ts +76 -0
  98. package/dist/esm/index.d.ts.map +1 -0
  99. package/dist/esm/index.js +76 -0
  100. package/dist/esm/index.js.map +1 -0
  101. package/dist/esm/internal/CanonicalJson.d.ts +44 -0
  102. package/dist/esm/internal/CanonicalJson.d.ts.map +1 -0
  103. package/dist/esm/internal/CanonicalJson.js +202 -0
  104. package/dist/esm/internal/CanonicalJson.js.map +1 -0
  105. package/dist/esm/internal/controlCharacters.d.ts +44 -0
  106. package/dist/esm/internal/controlCharacters.d.ts.map +1 -0
  107. package/dist/esm/internal/controlCharacters.js +65 -0
  108. package/dist/esm/internal/controlCharacters.js.map +1 -0
  109. package/dist/esm/internal/scorerLabel.d.ts +33 -0
  110. package/dist/esm/internal/scorerLabel.d.ts.map +1 -0
  111. package/dist/esm/internal/scorerLabel.js +33 -0
  112. package/dist/esm/internal/scorerLabel.js.map +1 -0
  113. package/dist/esm/internal/tupleKey.d.ts +20 -0
  114. package/dist/esm/internal/tupleKey.d.ts.map +1 -0
  115. package/dist/esm/internal/tupleKey.js +20 -0
  116. package/dist/esm/internal/tupleKey.js.map +1 -0
  117. package/docs/README.md +130 -0
  118. package/docs/api.md +626 -0
  119. package/docs/concepts/determinism.md +64 -0
  120. package/docs/concepts/fixed-suites.md +71 -0
  121. package/docs/concepts/step-keys.md +60 -0
  122. package/docs/guides/author-a-suite.md +115 -0
  123. package/docs/guides/gate-a-run-in-ci.md +129 -0
  124. package/docs/guides/maintain-a-baseline.md +91 -0
  125. package/docs/guides/run-a-suite.md +150 -0
  126. package/docs/installation.md +46 -0
  127. package/docs/quickstart.md +238 -0
  128. package/docs/troubleshooting.md +148 -0
  129. package/package.json +198 -3
  130. package/src/Baseline.ts +259 -0
  131. package/src/CaseExecutor.ts +104 -0
  132. package/src/EvalError.ts +74 -0
  133. package/src/Gate.ts +128 -0
  134. package/src/Regression.ts +314 -0
  135. package/src/Report.ts +268 -0
  136. package/src/Runner.ts +759 -0
  137. package/src/Suite.ts +358 -0
  138. package/src/Trials.ts +154 -0
  139. package/src/index.ts +76 -0
  140. package/src/internal/CanonicalJson.ts +234 -0
  141. package/src/internal/controlCharacters.ts +66 -0
  142. package/src/internal/scorerLabel.ts +35 -0
  143. package/src/internal/tupleKey.ts +20 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Trials.js","sourceRoot":"","sources":["../../src/Trials.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Repeated-run statistics for live model evaluations.\n *\n * A live case runs `n` times and passes `c` of them. {@link passAtK} is the\n * chance that at least one of `k` runs passes, the capability a best-of-k\n * caller sees; {@link passHatK} is the chance that all `k` pass, the\n * consistency a user sees on every attempt. Both are exact for sampling `k` of\n * the `n` recorded runs without replacement.\n *\n * Every function is pure and throws an `EvalError` with code `invalid_trials`\n * for counts outside the documented bounds.\n *\n * @since 0.1.0\n */\n\nimport { EvalError } from \"./EvalError.ts\"\n\n/**\n * Repeated-run statistics for one case.\n *\n * `rate` equals `passAt1`; `stderr` is the standard error of the pass rate,\n * `sqrt(rate * (1 - rate) / n)`, and 0 for a single run.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface Summary {\n readonly n: number\n readonly passes: number\n readonly rate: number\n readonly passAt1: number\n readonly passAtK: number\n readonly passHatK: number\n readonly stderr: number\n}\n\n/**\n * Repeated-run statistics across cases.\n *\n * `passAt1`, `passAtK`, and `passHatK` are means over cases, and `allPass` is\n * the fraction of cases whose every run passed.\n *\n * @since 0.1.0\n * @category models\n */\nexport interface Aggregate {\n readonly cases: number\n readonly passAt1: number\n readonly passAtK: number\n readonly passHatK: number\n readonly allPass: number\n readonly perCase: Readonly<Record<string, Summary>>\n}\n\nconst invalid = (message: string, path: string): EvalError => new EvalError({ code: \"invalid_trials\", message, path })\n\nconst validate = (n: number, c: number, k: number): void => {\n if (!Number.isInteger(n) || n < 1) throw invalid(`Run count must be a positive integer, received ${n}`, \"n\")\n if (!Number.isInteger(c) || c < 0 || c > n) {\n throw invalid(`Pass count must be an integer from 0 to ${n}, received ${c}`, \"c\")\n }\n if (!Number.isInteger(k) || k < 1 || k > n) throw invalid(`k must be an integer from 1 to ${n}, received ${k}`, \"k\")\n}\n\n/**\n * The unbiased pass@k estimator, `1 - C(n - c, k) / C(n, k)` (Chen et al.,\n * 2021): the chance that at least one of `k` runs drawn from `n` passes. It is\n * computed as a product, so large `n` neither overflows nor loses precision.\n *\n * Throws `invalid_trials` unless `n`, `c`, and `k` are integers with\n * `0 <= c <= n` and `1 <= k <= n`.\n *\n * @since 0.1.0\n * @category statistics\n */\nexport const passAtK = (n: number, c: number, k: number): number => {\n validate(n, c, k)\n if (n - c < k) return 1\n let fail = 1\n for (let i = n - c + 1; i <= n; i++) fail *= 1 - k / i\n return 1 - fail\n}\n\n/**\n * pass^k, `C(c, k) / C(n, k)`: the chance that all `k` runs drawn from `n`\n * without replacement pass, the consistency measure of tau-bench.\n *\n * Throws `invalid_trials` under the same bounds as {@link passAtK}.\n *\n * @since 0.1.0\n * @category statistics\n */\nexport const passHatK = (n: number, c: number, k: number): number => {\n validate(n, c, k)\n let all = 1\n for (let i = 0; i < k; i++) all *= (c - i) / (n - i)\n return Math.max(0, all)\n}\n\n/**\n * Summarizes one case's run outcomes at `k`.\n *\n * Throws `invalid_trials` for no outcomes or a `k` outside `1..n`.\n *\n * @since 0.1.0\n * @category statistics\n */\nexport const summarize = (outcomes: ReadonlyArray<boolean>, k: number): Summary => {\n const n = outcomes.length\n const passes = outcomes.filter(Boolean).length\n validate(n, passes, k)\n const rate = passes / n\n return {\n n,\n passes,\n rate,\n passAt1: rate,\n passAtK: passAtK(n, passes, k),\n passHatK: passHatK(n, passes, k),\n stderr: n <= 1 ? 0 : Math.sqrt(rate * (1 - rate) / n)\n }\n}\n\nconst mean = (values: ReadonlyArray<number>): number =>\n values.length === 0 ? 0 : values.reduce((total, value) => total + value, 0) / values.length\n\n/**\n * Summarizes every case at `k` and averages over cases.\n *\n * A case with fewer than `k` runs is summarized at `k = n`, its own run\n * count, so a short case still contributes rather than failing the whole\n * aggregate. No cases aggregate to zeros. Throws `invalid_trials` for a case\n * with no runs or a `k` that is not a positive integer.\n *\n * @since 0.1.0\n * @category statistics\n */\nexport const aggregate = (cases: Readonly<Record<string, ReadonlyArray<boolean>>>, k: number): Aggregate => {\n if (!Number.isInteger(k) || k < 1) throw invalid(`k must be a positive integer, received ${k}`, \"k\")\n const perCase: Record<string, Summary> = {}\n for (const [name, outcomes] of Object.entries(cases)) {\n if (outcomes.length === 0) throw invalid(`Case '${name}' has no runs`, `cases['${name}']`)\n perCase[name] = summarize(outcomes, Math.min(k, outcomes.length))\n }\n const summaries = Object.values(perCase)\n return {\n cases: summaries.length,\n passAt1: mean(summaries.map((summary) => summary.passAt1)),\n passAtK: mean(summaries.map((summary) => summary.passAtK)),\n passHatK: mean(summaries.map((summary) => summary.passHatK)),\n allPass: mean(summaries.map((summary) => summary.passes === summary.n ? 1 : 0)),\n perCase\n }\n}\n"]}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Fixed-suite evaluation tools for flows.
3
+ *
4
+ * The pipeline runs in one direction: a {@link Suite} declares fixed cases and
5
+ * the scorers bound to them, a {@link Runner} executes them through an injected
6
+ * {@link CaseExecutor} and grades the executions, {@link Baseline} records what
7
+ * a run scored, {@link Regression} compares the next run with that record,
8
+ * {@link Report} renders the comparison, and {@link Gate} turns it into a CI
9
+ * exit code.
10
+ *
11
+ * @since 0.1.0
12
+ */
13
+ /**
14
+ * Typed evaluation failures and the stable codes a caller branches on.
15
+ *
16
+ * @since 0.1.0
17
+ * @category errors
18
+ */
19
+ export * as EvalError from "./EvalError.ts";
20
+ /**
21
+ * Fixed suite declarations and their JSON Lines fixture format.
22
+ *
23
+ * @since 0.1.0
24
+ * @category suites
25
+ */
26
+ export * as Suite from "./Suite.ts";
27
+ /**
28
+ * The injectable boundary that executes one case against a target flow.
29
+ *
30
+ * @since 0.1.0
31
+ * @category services
32
+ */
33
+ export * as CaseExecutor from "./CaseExecutor.ts";
34
+ /**
35
+ * Deterministic suite execution and bound scorer evaluation.
36
+ *
37
+ * @since 0.1.0
38
+ * @category runners
39
+ */
40
+ export * as Runner from "./Runner.ts";
41
+ /**
42
+ * Committed baselines: what a suite used to score.
43
+ *
44
+ * @since 0.1.0
45
+ * @category baselines
46
+ */
47
+ export * as Baseline from "./Baseline.ts";
48
+ /**
49
+ * Step-key-aware comparison of a run against a baseline.
50
+ *
51
+ * @since 0.1.0
52
+ * @category regression
53
+ */
54
+ export * as Regression from "./Regression.ts";
55
+ /**
56
+ * Canonical JSON and Markdown renderings of a comparison.
57
+ *
58
+ * @since 0.1.0
59
+ * @category reports
60
+ */
61
+ export * as Report from "./Report.ts";
62
+ /**
63
+ * Score thresholds and the CI exit grade a comparison earns.
64
+ *
65
+ * @since 0.1.0
66
+ * @category gates
67
+ */
68
+ export * as Gate from "./Gate.ts";
69
+ /**
70
+ * Repeated-run statistics for live evaluations: pass@k and pass^k.
71
+ *
72
+ * @since 0.1.0
73
+ * @category statistics
74
+ */
75
+ export * as Trials from "./Trials.ts";
76
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C;;;;;GAKG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC;;;;;GAKG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AACjD;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC;;;;;GAKG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC;;;;;GAKG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC;;;;;GAKG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Fixed-suite evaluation tools for flows.
3
+ *
4
+ * The pipeline runs in one direction: a {@link Suite} declares fixed cases and
5
+ * the scorers bound to them, a {@link Runner} executes them through an injected
6
+ * {@link CaseExecutor} and grades the executions, {@link Baseline} records what
7
+ * a run scored, {@link Regression} compares the next run with that record,
8
+ * {@link Report} renders the comparison, and {@link Gate} turns it into a CI
9
+ * exit code.
10
+ *
11
+ * @since 0.1.0
12
+ */
13
+ /**
14
+ * Typed evaluation failures and the stable codes a caller branches on.
15
+ *
16
+ * @since 0.1.0
17
+ * @category errors
18
+ */
19
+ export * as EvalError from "./EvalError.js";
20
+ /**
21
+ * Fixed suite declarations and their JSON Lines fixture format.
22
+ *
23
+ * @since 0.1.0
24
+ * @category suites
25
+ */
26
+ export * as Suite from "./Suite.js";
27
+ /**
28
+ * The injectable boundary that executes one case against a target flow.
29
+ *
30
+ * @since 0.1.0
31
+ * @category services
32
+ */
33
+ export * as CaseExecutor from "./CaseExecutor.js";
34
+ /**
35
+ * Deterministic suite execution and bound scorer evaluation.
36
+ *
37
+ * @since 0.1.0
38
+ * @category runners
39
+ */
40
+ export * as Runner from "./Runner.js";
41
+ /**
42
+ * Committed baselines: what a suite used to score.
43
+ *
44
+ * @since 0.1.0
45
+ * @category baselines
46
+ */
47
+ export * as Baseline from "./Baseline.js";
48
+ /**
49
+ * Step-key-aware comparison of a run against a baseline.
50
+ *
51
+ * @since 0.1.0
52
+ * @category regression
53
+ */
54
+ export * as Regression from "./Regression.js";
55
+ /**
56
+ * Canonical JSON and Markdown renderings of a comparison.
57
+ *
58
+ * @since 0.1.0
59
+ * @category reports
60
+ */
61
+ export * as Report from "./Report.js";
62
+ /**
63
+ * Score thresholds and the CI exit grade a comparison earns.
64
+ *
65
+ * @since 0.1.0
66
+ * @category gates
67
+ */
68
+ export * as Gate from "./Gate.js";
69
+ /**
70
+ * Repeated-run statistics for live evaluations: pass@k and pass^k.
71
+ *
72
+ * @since 0.1.0
73
+ * @category statistics
74
+ */
75
+ export * as Trials from "./Trials.js";
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAC3C;;;;;GAKG;AACH,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AACnC;;;;;GAKG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AACjD;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC;;;;;GAKG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AACzC;;;;;GAKG;AACH,OAAO,KAAK,UAAU,MAAM,iBAAiB,CAAA;AAC7C;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA;AACrC;;;;;GAKG;AACH,OAAO,KAAK,IAAI,MAAM,WAAW,CAAA;AACjC;;;;;GAKG;AACH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAA","sourcesContent":["/**\n * Fixed-suite evaluation tools for flows.\n *\n * The pipeline runs in one direction: a {@link Suite} declares fixed cases and\n * the scorers bound to them, a {@link Runner} executes them through an injected\n * {@link CaseExecutor} and grades the executions, {@link Baseline} records what\n * a run scored, {@link Regression} compares the next run with that record,\n * {@link Report} renders the comparison, and {@link Gate} turns it into a CI\n * exit code.\n *\n * @since 0.1.0\n */\n\n/**\n * Typed evaluation failures and the stable codes a caller branches on.\n *\n * @since 0.1.0\n * @category errors\n */\nexport * as EvalError from \"./EvalError.ts\"\n/**\n * Fixed suite declarations and their JSON Lines fixture format.\n *\n * @since 0.1.0\n * @category suites\n */\nexport * as Suite from \"./Suite.ts\"\n/**\n * The injectable boundary that executes one case against a target flow.\n *\n * @since 0.1.0\n * @category services\n */\nexport * as CaseExecutor from \"./CaseExecutor.ts\"\n/**\n * Deterministic suite execution and bound scorer evaluation.\n *\n * @since 0.1.0\n * @category runners\n */\nexport * as Runner from \"./Runner.ts\"\n/**\n * Committed baselines: what a suite used to score.\n *\n * @since 0.1.0\n * @category baselines\n */\nexport * as Baseline from \"./Baseline.ts\"\n/**\n * Step-key-aware comparison of a run against a baseline.\n *\n * @since 0.1.0\n * @category regression\n */\nexport * as Regression from \"./Regression.ts\"\n/**\n * Canonical JSON and Markdown renderings of a comparison.\n *\n * @since 0.1.0\n * @category reports\n */\nexport * as Report from \"./Report.ts\"\n/**\n * Score thresholds and the CI exit grade a comparison earns.\n *\n * @since 0.1.0\n * @category gates\n */\nexport * as Gate from \"./Gate.ts\"\n/**\n * Repeated-run statistics for live evaluations: pass@k and pass^k.\n *\n * @since 0.1.0\n * @category statistics\n */\nexport * as Trials from \"./Trials.ts\"\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The one canonical JSON encoder both serializers use.
3
+ *
4
+ * A report embeds the raw output of an arbitrary target flow, so the value
5
+ * being serialized is not JSON and cannot be assumed to be finite, acyclic, or
6
+ * even readable. A recursive walker over `JSON.stringify` answered that with a
7
+ * `RangeError` thrown out of a function typed `string`. This encoder is total
8
+ * instead: every shape JSON cannot express is replaced by a marker that names
9
+ * what was there, so a report of a broken run is still a report.
10
+ *
11
+ * @since 0.1.0
12
+ */
13
+ /**
14
+ * Options accepted by {@link encode}.
15
+ *
16
+ * @since 0.1.0
17
+ * @private
18
+ */
19
+ interface Options {
20
+ /** Cap on embedded strings; `undefined` keeps them whole. */
21
+ readonly maxStringLength?: number | undefined;
22
+ /** Cap on values visited; defaults to {@link maxNodes}. */
23
+ readonly maxNodes?: number | undefined;
24
+ /** Cap on encoded output length; defaults to {@link maxBytes}. */
25
+ readonly maxBytes?: number | undefined;
26
+ }
27
+ /**
28
+ * The canonical JSON encoder: its limits, key order, and serializers.
29
+ *
30
+ * @since 0.1.0
31
+ * @private
32
+ */
33
+ export declare const CanonicalJson: {
34
+ readonly maxDepth: 64;
35
+ readonly maxStringLength: 8192;
36
+ readonly maxNodes: 2000000;
37
+ readonly maxBytes: 33554432;
38
+ readonly budgetExceeded: "[budget exceeded]";
39
+ readonly compareText: (left: string, right: string) => number;
40
+ readonly encode: (value: unknown, options?: Options) => unknown;
41
+ readonly stringify: (value: unknown, options?: Options) => string;
42
+ };
43
+ export {};
44
+ //# sourceMappingURL=CanonicalJson.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CanonicalJson.d.ts","sourceRoot":"","sources":["../../../src/internal/CanonicalJson.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiDH;;;;;GAKG;AACH,UAAU,OAAO;IACf,6DAA6D;IAC7D,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7C,2DAA2D;IAC3D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACvC;AAiJD;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;iCAzIC,MAAM,SAAS,MAAM,KAAG,MAAM;6BAqHlC,OAAO,YAAW,OAAO,KAAQ,OAAO;gCAYrC,OAAO,YAAW,OAAO,KAAQ,MAAM;CAiBvD,CAAA"}
@@ -0,0 +1,202 @@
1
+ /**
2
+ * The one canonical JSON encoder both serializers use.
3
+ *
4
+ * A report embeds the raw output of an arbitrary target flow, so the value
5
+ * being serialized is not JSON and cannot be assumed to be finite, acyclic, or
6
+ * even readable. A recursive walker over `JSON.stringify` answered that with a
7
+ * `RangeError` thrown out of a function typed `string`. This encoder is total
8
+ * instead: every shape JSON cannot express is replaced by a marker that names
9
+ * what was there, so a report of a broken run is still a report.
10
+ *
11
+ * @since 0.1.0
12
+ */
13
+ /**
14
+ * The nesting depth beyond which a value is replaced by a marker.
15
+ *
16
+ * @since 0.1.0
17
+ * @private
18
+ */
19
+ const maxDepth = 64;
20
+ /**
21
+ * Default cap on an embedded string, in UTF-16 code units.
22
+ *
23
+ * @since 0.1.0
24
+ * @private
25
+ */
26
+ const maxStringLength = 8192;
27
+ /**
28
+ * Total number of values one encode may visit.
29
+ *
30
+ * The cycle detector forgets a value once it leaves it, which is what keeps a
31
+ * value referenced twice from being reported as a cycle. The cost is that a
32
+ * shared acyclic graph is re-expanded at every reference, so `n` nested
33
+ * two-child wrappers over one shared child expand to `2^n` values while
34
+ * staying far below {@link maxDepth}. The ceiling is roughly three times what
35
+ * a suite at its declared case limit produces.
36
+ *
37
+ * @since 0.1.0
38
+ * @private
39
+ */
40
+ const maxNodes = 2_000_000;
41
+ /**
42
+ * Approximate cap on the encoded output, in UTF-16 code units.
43
+ *
44
+ * @since 0.1.0
45
+ * @private
46
+ */
47
+ const maxBytes = 33_554_432;
48
+ /**
49
+ * Marker left where a total budget ran out.
50
+ *
51
+ * @since 0.1.0
52
+ * @private
53
+ */
54
+ const budgetExceeded = "[budget exceeded]";
55
+ /**
56
+ * Orders keys by UTF-16 code unit, never by locale.
57
+ *
58
+ * @since 0.1.0
59
+ * @private
60
+ */
61
+ const compareText = (left, right) => left < right ? -1 : left > right ? 1 : 0;
62
+ const truncate = (value, limit) => limit === undefined || value.length <= limit
63
+ ? value
64
+ : `${value.slice(0, limit)}[truncated ${value.length - limit} chars]`;
65
+ const primitive = (value, options) => {
66
+ if (typeof value === "string")
67
+ return truncate(value, options.maxStringLength);
68
+ if (typeof value === "number") {
69
+ if (Number.isNaN(value))
70
+ return "[NaN]";
71
+ if (value === Number.POSITIVE_INFINITY)
72
+ return "[Infinity]";
73
+ if (value === Number.NEGATIVE_INFINITY)
74
+ return "[-Infinity]";
75
+ return Object.is(value, -0) ? 0 : value;
76
+ }
77
+ if (typeof value === "bigint")
78
+ return `[bigint ${value}]`;
79
+ if (typeof value === "function")
80
+ return "[function]";
81
+ if (typeof value === "symbol")
82
+ return "[symbol]";
83
+ return value;
84
+ };
85
+ const unreadable = (cause) => {
86
+ try {
87
+ return `[unreadable: ${String(cause)}]`;
88
+ }
89
+ catch {
90
+ return "[unreadable]";
91
+ }
92
+ };
93
+ const entriesOf = (value) => Object.keys(value).map((key) => {
94
+ try {
95
+ return [key, value[key]];
96
+ }
97
+ catch (cause) {
98
+ return [key, unreadable(cause)];
99
+ }
100
+ });
101
+ const objectOf = (entries, depth, seen, options, budget) => Object.fromEntries(entries
102
+ .filter(([, entry]) => entry !== undefined)
103
+ .sort(([left], [right]) => compareText(left, right))
104
+ .map(([key, entry]) => {
105
+ // The key is rendered too, so a wide object of long keys spends the
106
+ // byte budget even when every value is a marker.
107
+ budget.bytes -= key.length + 3;
108
+ return [key, walk(entry, depth + 1, seen, options, budget)];
109
+ }));
110
+ const walk = (value, depth, seen, options, budget) => {
111
+ if (budget.nodes <= 0 || budget.bytes <= 0)
112
+ return budgetExceeded;
113
+ budget.nodes -= 1;
114
+ if (value === null || typeof value !== "object") {
115
+ const encoded = primitive(value, options);
116
+ budget.bytes -= typeof encoded === "string" ? encoded.length + 2 : 8;
117
+ return encoded;
118
+ }
119
+ if (seen.has(value))
120
+ return "[circular]";
121
+ if (depth > maxDepth)
122
+ return "[depth exceeded]";
123
+ budget.bytes -= 2;
124
+ try {
125
+ if (value instanceof Date)
126
+ return Number.isNaN(value.getTime()) ? "[invalid Date]" : value.toISOString();
127
+ seen.add(value);
128
+ try {
129
+ if (value instanceof Error) {
130
+ const properties = new Map(entriesOf(value));
131
+ properties.set("name", String(value.name));
132
+ properties.set("message", String(value.message));
133
+ return objectOf([...properties], depth, seen, options, budget);
134
+ }
135
+ if (Array.isArray(value))
136
+ return value.map((entry) => walk(entry, depth + 1, seen, options, budget));
137
+ if (value instanceof Set)
138
+ return [...value].map((entry) => walk(entry, depth + 1, seen, options, budget));
139
+ if (value instanceof Map) {
140
+ return [...value.entries()]
141
+ .map(([key, entry]) => [walk(key, depth + 1, seen, options, budget), walk(entry, depth + 1, seen, options, budget)]);
142
+ }
143
+ return objectOf(entriesOf(value), depth, seen, options, budget);
144
+ }
145
+ finally {
146
+ seen.delete(value);
147
+ }
148
+ }
149
+ catch (cause) {
150
+ return unreadable(cause);
151
+ }
152
+ };
153
+ /**
154
+ * Rewrites a value into a shape `JSON.stringify` renders deterministically.
155
+ *
156
+ * Object keys are sorted by code unit, `-0` becomes `0`, and `undefined`
157
+ * members are dropped. Everything JSON cannot express becomes a bracketed
158
+ * marker rather than a throw or a silent `null`: `[circular]`,
159
+ * `[depth exceeded]`, `[NaN]`, `[Infinity]`, `[-Infinity]`, `[bigint n]`,
160
+ * `[function]`, `[symbol]`, and `[unreadable: …]` when a foreign operation
161
+ * throws. An `Error` becomes an object containing its own enumerable fields
162
+ * plus `name` and `message`, so typed error fields survive serialization. A
163
+ * `Date` becomes its ISO string, a `Set` an array, and a `Map` an array of
164
+ * key/value pairs.
165
+ *
166
+ * Traversal is bounded in total, not only per branch: once {@link maxNodes}
167
+ * values or {@link maxBytes} output code units are spent, every remaining
168
+ * value becomes `[budget exceeded]`. The budget is spent in traversal order,
169
+ * and traversal order is fixed by the key sort, so one value always truncates
170
+ * at the same place.
171
+ *
172
+ * @since 0.1.0
173
+ * @private
174
+ */
175
+ const encode = (value, options = {}) => walk(value, 0, new Set(), options, {
176
+ nodes: options.maxNodes ?? maxNodes,
177
+ bytes: options.maxBytes ?? maxBytes
178
+ });
179
+ /**
180
+ * Serializes a value as canonical JSON with a trailing newline.
181
+ *
182
+ * @since 0.1.0
183
+ * @private
184
+ */
185
+ const stringify = (value, options = {}) => `${JSON.stringify(encode(value, options))}\n`;
186
+ /**
187
+ * The canonical JSON encoder: its limits, key order, and serializers.
188
+ *
189
+ * @since 0.1.0
190
+ * @private
191
+ */
192
+ export const CanonicalJson = {
193
+ maxDepth,
194
+ maxStringLength,
195
+ maxNodes,
196
+ maxBytes,
197
+ budgetExceeded,
198
+ compareText,
199
+ encode,
200
+ stringify
201
+ };
202
+ //# sourceMappingURL=CanonicalJson.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CanonicalJson.js","sourceRoot":"","sources":["../../../src/internal/CanonicalJson.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The one canonical JSON encoder both serializers use.\n *\n * A report embeds the raw output of an arbitrary target flow, so the value\n * being serialized is not JSON and cannot be assumed to be finite, acyclic, or\n * even readable. A recursive walker over `JSON.stringify` answered that with a\n * `RangeError` thrown out of a function typed `string`. This encoder is total\n * instead: every shape JSON cannot express is replaced by a marker that names\n * what was there, so a report of a broken run is still a report.\n *\n * @since 0.1.0\n */\n\n/**\n * The nesting depth beyond which a value is replaced by a marker.\n *\n * @since 0.1.0\n * @private\n */\nconst maxDepth = 64\n\n/**\n * Default cap on an embedded string, in UTF-16 code units.\n *\n * @since 0.1.0\n * @private\n */\nconst maxStringLength = 8192\n\n/**\n * Total number of values one encode may visit.\n *\n * The cycle detector forgets a value once it leaves it, which is what keeps a\n * value referenced twice from being reported as a cycle. The cost is that a\n * shared acyclic graph is re-expanded at every reference, so `n` nested\n * two-child wrappers over one shared child expand to `2^n` values while\n * staying far below {@link maxDepth}. The ceiling is roughly three times what\n * a suite at its declared case limit produces.\n *\n * @since 0.1.0\n * @private\n */\nconst maxNodes = 2_000_000\n\n/**\n * Approximate cap on the encoded output, in UTF-16 code units.\n *\n * @since 0.1.0\n * @private\n */\nconst maxBytes = 33_554_432\n\n/**\n * Marker left where a total budget ran out.\n *\n * @since 0.1.0\n * @private\n */\nconst budgetExceeded = \"[budget exceeded]\"\n\n/**\n * Options accepted by {@link encode}.\n *\n * @since 0.1.0\n * @private\n */\ninterface Options {\n /** Cap on embedded strings; `undefined` keeps them whole. */\n readonly maxStringLength?: number | undefined\n /** Cap on values visited; defaults to {@link maxNodes}. */\n readonly maxNodes?: number | undefined\n /** Cap on encoded output length; defaults to {@link maxBytes}. */\n readonly maxBytes?: number | undefined\n}\n\n/** Remaining total work, decremented in traversal order so truncation is stable. */\ninterface Budget {\n nodes: number\n bytes: number\n}\n\n/**\n * Orders keys by UTF-16 code unit, never by locale.\n *\n * @since 0.1.0\n * @private\n */\nconst compareText = (left: string, right: string): number => left < right ? -1 : left > right ? 1 : 0\n\nconst truncate = (value: string, limit: number | undefined): string =>\n limit === undefined || value.length <= limit\n ? value\n : `${value.slice(0, limit)}[truncated ${value.length - limit} chars]`\n\nconst primitive = (value: unknown, options: Options): unknown => {\n if (typeof value === \"string\") return truncate(value, options.maxStringLength)\n if (typeof value === \"number\") {\n if (Number.isNaN(value)) return \"[NaN]\"\n if (value === Number.POSITIVE_INFINITY) return \"[Infinity]\"\n if (value === Number.NEGATIVE_INFINITY) return \"[-Infinity]\"\n return Object.is(value, -0) ? 0 : value\n }\n if (typeof value === \"bigint\") return `[bigint ${value}]`\n if (typeof value === \"function\") return \"[function]\"\n if (typeof value === \"symbol\") return \"[symbol]\"\n return value\n}\n\nconst unreadable = (cause: unknown): string => {\n try {\n return `[unreadable: ${String(cause)}]`\n } catch {\n return \"[unreadable]\"\n }\n}\n\nconst entriesOf = (value: object): ReadonlyArray<readonly [string, unknown]> =>\n Object.keys(value).map((key) => {\n try {\n return [key, (value as { readonly [key: string]: unknown })[key]] as const\n } catch (cause) {\n return [key, unreadable(cause)] as const\n }\n })\n\nconst objectOf = (\n entries: ReadonlyArray<readonly [string, unknown]>,\n depth: number,\n seen: Set<object>,\n options: Options,\n budget: Budget\n): object =>\n Object.fromEntries(\n entries\n .filter(([, entry]) => entry !== undefined)\n .sort(([left], [right]) => compareText(left, right))\n .map(([key, entry]) => {\n // The key is rendered too, so a wide object of long keys spends the\n // byte budget even when every value is a marker.\n budget.bytes -= key.length + 3\n return [key, walk(entry, depth + 1, seen, options, budget)]\n })\n )\n\nconst walk = (value: unknown, depth: number, seen: Set<object>, options: Options, budget: Budget): unknown => {\n if (budget.nodes <= 0 || budget.bytes <= 0) return budgetExceeded\n budget.nodes -= 1\n if (value === null || typeof value !== \"object\") {\n const encoded = primitive(value, options)\n budget.bytes -= typeof encoded === \"string\" ? encoded.length + 2 : 8\n return encoded\n }\n if (seen.has(value)) return \"[circular]\"\n if (depth > maxDepth) return \"[depth exceeded]\"\n budget.bytes -= 2\n try {\n if (value instanceof Date) return Number.isNaN(value.getTime()) ? \"[invalid Date]\" : value.toISOString()\n seen.add(value)\n try {\n if (value instanceof Error) {\n const properties = new Map(entriesOf(value))\n properties.set(\"name\", String(value.name))\n properties.set(\"message\", String(value.message))\n return objectOf([...properties], depth, seen, options, budget)\n }\n if (Array.isArray(value)) return value.map((entry) => walk(entry, depth + 1, seen, options, budget))\n if (value instanceof Set) return [...value].map((entry) => walk(entry, depth + 1, seen, options, budget))\n if (value instanceof Map) {\n return [...value.entries()]\n .map((\n [key, entry]\n ) => [walk(key, depth + 1, seen, options, budget), walk(entry, depth + 1, seen, options, budget)])\n }\n return objectOf(entriesOf(value), depth, seen, options, budget)\n } finally {\n seen.delete(value)\n }\n } catch (cause) {\n return unreadable(cause)\n }\n}\n\n/**\n * Rewrites a value into a shape `JSON.stringify` renders deterministically.\n *\n * Object keys are sorted by code unit, `-0` becomes `0`, and `undefined`\n * members are dropped. Everything JSON cannot express becomes a bracketed\n * marker rather than a throw or a silent `null`: `[circular]`,\n * `[depth exceeded]`, `[NaN]`, `[Infinity]`, `[-Infinity]`, `[bigint n]`,\n * `[function]`, `[symbol]`, and `[unreadable: …]` when a foreign operation\n * throws. An `Error` becomes an object containing its own enumerable fields\n * plus `name` and `message`, so typed error fields survive serialization. A\n * `Date` becomes its ISO string, a `Set` an array, and a `Map` an array of\n * key/value pairs.\n *\n * Traversal is bounded in total, not only per branch: once {@link maxNodes}\n * values or {@link maxBytes} output code units are spent, every remaining\n * value becomes `[budget exceeded]`. The budget is spent in traversal order,\n * and traversal order is fixed by the key sort, so one value always truncates\n * at the same place.\n *\n * @since 0.1.0\n * @private\n */\nconst encode = (value: unknown, options: Options = {}): unknown =>\n walk(value, 0, new Set<object>(), options, {\n nodes: options.maxNodes ?? maxNodes,\n bytes: options.maxBytes ?? maxBytes\n })\n\n/**\n * Serializes a value as canonical JSON with a trailing newline.\n *\n * @since 0.1.0\n * @private\n */\nconst stringify = (value: unknown, options: Options = {}): string => `${JSON.stringify(encode(value, options))}\\n`\n\n/**\n * The canonical JSON encoder: its limits, key order, and serializers.\n *\n * @since 0.1.0\n * @private\n */\nexport const CanonicalJson = {\n maxDepth,\n maxStringLength,\n maxNodes,\n maxBytes,\n budgetExceeded,\n compareText,\n encode,\n stringify\n} as const\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The one control-character rule every trust boundary in this package shares.
3
+ *
4
+ * A control character in a name corrupts a Markdown report and a CI log line:
5
+ * a newline lets the value print its own line, which on GitHub Actions is a
6
+ * workflow command the runner executes, an ESC or a C1 CSI (U+009B) sequence
7
+ * rewrites the visible log, and a Unicode bidirectional control reorders what
8
+ * a reader sees. Values an author declares (suite and case names, baseline
9
+ * suites and records) are rejected where they enter the system; values a
10
+ * target returns at runtime (step keys, failure messages) are flattened
11
+ * instead, so a broken target stays a readable failure rather than a rejected
12
+ * run.
13
+ *
14
+ * The rule covers C0 controls (U+0000 through U+001F), DEL (U+007F), C1
15
+ * controls (U+0080 through U+009F), and the bidirectional controls U+061C,
16
+ * U+200E, U+200F, U+202A through U+202E, and U+2066 through U+2069.
17
+ *
18
+ * @since 0.1.0
19
+ */
20
+ /**
21
+ * Whether one code point falls under the control-character rule.
22
+ *
23
+ * @since 0.1.0
24
+ * @private
25
+ */
26
+ export declare const isControlCodePoint: (code: number) => boolean;
27
+ /**
28
+ * Names the first control character in `value`, or `undefined` when there is
29
+ * none.
30
+ *
31
+ * @since 0.1.0
32
+ * @private
33
+ */
34
+ export declare const controlCharacter: (value: string) => string | undefined;
35
+ /**
36
+ * Replaces every control character with a space, the way a report cell does,
37
+ * so a hostile string cannot emit its own log line, terminal escape, or
38
+ * reordered text.
39
+ *
40
+ * @since 0.1.0
41
+ * @private
42
+ */
43
+ export declare const flattenControlCharacters: (value: string) => string;
44
+ //# sourceMappingURL=controlCharacters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlCharacters.d.ts","sourceRoot":"","sources":["../../../src/internal/controlCharacters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAAI,MAAM,MAAM,KAAG,OAOd,CAAA;AAEpC;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,GAAI,OAAO,MAAM,KAAG,MAAM,GAAG,SAMzD,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,wBAAwB,GAAI,OAAO,MAAM,KAAG,MAOxD,CAAA"}
@@ -0,0 +1,65 @@
1
+ /**
2
+ * The one control-character rule every trust boundary in this package shares.
3
+ *
4
+ * A control character in a name corrupts a Markdown report and a CI log line:
5
+ * a newline lets the value print its own line, which on GitHub Actions is a
6
+ * workflow command the runner executes, an ESC or a C1 CSI (U+009B) sequence
7
+ * rewrites the visible log, and a Unicode bidirectional control reorders what
8
+ * a reader sees. Values an author declares (suite and case names, baseline
9
+ * suites and records) are rejected where they enter the system; values a
10
+ * target returns at runtime (step keys, failure messages) are flattened
11
+ * instead, so a broken target stays a readable failure rather than a rejected
12
+ * run.
13
+ *
14
+ * The rule covers C0 controls (U+0000 through U+001F), DEL (U+007F), C1
15
+ * controls (U+0080 through U+009F), and the bidirectional controls U+061C,
16
+ * U+200E, U+200F, U+202A through U+202E, and U+2066 through U+2069.
17
+ *
18
+ * @since 0.1.0
19
+ */
20
+ /**
21
+ * Whether one code point falls under the control-character rule.
22
+ *
23
+ * @since 0.1.0
24
+ * @private
25
+ */
26
+ export const isControlCodePoint = (code) => code < 0x20 ||
27
+ (code >= 0x7f && code <= 0x9f) ||
28
+ code === 0x061c ||
29
+ code === 0x200e ||
30
+ code === 0x200f ||
31
+ (code >= 0x202a && code <= 0x202e) ||
32
+ (code >= 0x2066 && code <= 0x2069);
33
+ /**
34
+ * Names the first control character in `value`, or `undefined` when there is
35
+ * none.
36
+ *
37
+ * @since 0.1.0
38
+ * @private
39
+ */
40
+ export const controlCharacter = (value) => {
41
+ for (const character of value) {
42
+ const code = character.codePointAt(0);
43
+ if (isControlCodePoint(code))
44
+ return `U+${code.toString(16).toUpperCase().padStart(4, "0")}`;
45
+ }
46
+ return undefined;
47
+ };
48
+ /**
49
+ * Replaces every control character with a space, the way a report cell does,
50
+ * so a hostile string cannot emit its own log line, terminal escape, or
51
+ * reordered text.
52
+ *
53
+ * @since 0.1.0
54
+ * @private
55
+ */
56
+ export const flattenControlCharacters = (value) => {
57
+ if (controlCharacter(value) === undefined)
58
+ return value;
59
+ let flattened = "";
60
+ for (const character of value) {
61
+ flattened += isControlCodePoint(character.codePointAt(0)) ? " " : character;
62
+ }
63
+ return flattened;
64
+ };
65
+ //# sourceMappingURL=controlCharacters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"controlCharacters.js","sourceRoot":"","sources":["../../../src/internal/controlCharacters.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAY,EAAW,EAAE,CAC1D,IAAI,GAAG,IAAI;IACX,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC;IAC9B,IAAI,KAAK,MAAM;IACf,IAAI,KAAK,MAAM;IACf,IAAI,KAAK,MAAM;IACf,CAAC,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC;IAClC,CAAC,IAAI,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,CAAA;AAEpC;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAsB,EAAE;IACpE,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,SAAS,CAAC,WAAW,CAAC,CAAC,CAAE,CAAA;QACtC,IAAI,kBAAkB,CAAC,IAAI,CAAC;YAAE,OAAO,KAAK,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAAA;IAC9F,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,KAAa,EAAU,EAAE;IAChE,IAAI,gBAAgB,CAAC,KAAK,CAAC,KAAK,SAAS;QAAE,OAAO,KAAK,CAAA;IACvD,IAAI,SAAS,GAAG,EAAE,CAAA;IAClB,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,SAAS,IAAI,kBAAkB,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAA;IAC9E,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA","sourcesContent":["/**\n * The one control-character rule every trust boundary in this package shares.\n *\n * A control character in a name corrupts a Markdown report and a CI log line:\n * a newline lets the value print its own line, which on GitHub Actions is a\n * workflow command the runner executes, an ESC or a C1 CSI (U+009B) sequence\n * rewrites the visible log, and a Unicode bidirectional control reorders what\n * a reader sees. Values an author declares (suite and case names, baseline\n * suites and records) are rejected where they enter the system; values a\n * target returns at runtime (step keys, failure messages) are flattened\n * instead, so a broken target stays a readable failure rather than a rejected\n * run.\n *\n * The rule covers C0 controls (U+0000 through U+001F), DEL (U+007F), C1\n * controls (U+0080 through U+009F), and the bidirectional controls U+061C,\n * U+200E, U+200F, U+202A through U+202E, and U+2066 through U+2069.\n *\n * @since 0.1.0\n */\n\n/**\n * Whether one code point falls under the control-character rule.\n *\n * @since 0.1.0\n * @private\n */\nexport const isControlCodePoint = (code: number): boolean =>\n code < 0x20 ||\n (code >= 0x7f && code <= 0x9f) ||\n code === 0x061c ||\n code === 0x200e ||\n code === 0x200f ||\n (code >= 0x202a && code <= 0x202e) ||\n (code >= 0x2066 && code <= 0x2069)\n\n/**\n * Names the first control character in `value`, or `undefined` when there is\n * none.\n *\n * @since 0.1.0\n * @private\n */\nexport const controlCharacter = (value: string): string | undefined => {\n for (const character of value) {\n const code = character.codePointAt(0)!\n if (isControlCodePoint(code)) return `U+${code.toString(16).toUpperCase().padStart(4, \"0\")}`\n }\n return undefined\n}\n\n/**\n * Replaces every control character with a space, the way a report cell does,\n * so a hostile string cannot emit its own log line, terminal escape, or\n * reordered text.\n *\n * @since 0.1.0\n * @private\n */\nexport const flattenControlCharacters = (value: string): string => {\n if (controlCharacter(value) === undefined) return value\n let flattened = \"\"\n for (const character of value) {\n flattened += isControlCodePoint(character.codePointAt(0)!) ? \" \" : character\n }\n return flattened\n}\n"]}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The one scorer label rule every rendering in this package shares.
3
+ *
4
+ * A scorer has two identities: `scorer` is the key, a digest of the scorer's
5
+ * own declaration and the only thing a baseline matches on, and `scorerName`
6
+ * is the readable name carried beside it. A digest is unreadable in a report
7
+ * cell, a CI log line, or a diagnostic, so each of them prints the name with
8
+ * the first eight key characters instead. Without a name there is nothing to
9
+ * abbreviate the key down to, so the label is the key itself: a truncated
10
+ * digest and no name would leave a reader nothing to match a baseline record
11
+ * on. One rule is what keeps a report from naming one scorer two ways, an
12
+ * inconclusive reason built by the runner and a Missing row built by the
13
+ * reporter agree.
14
+ *
15
+ * @since 0.1.0
16
+ */
17
+ /**
18
+ * Renders `name (first 8 of the key)`, or the bare key when the scorer carries
19
+ * no name.
20
+ *
21
+ * Every flow carries a name, so a scorer a run holds always has one. A stored
22
+ * observation may not: a baseline recorded before a scorer was named, or one
23
+ * written by another tool, carries the key alone, and it labels as the key
24
+ * rather than as a blank followed by a digest.
25
+ *
26
+ * @since 0.1.0
27
+ * @private
28
+ */
29
+ export declare const scorerLabel: (identity: {
30
+ readonly scorer: string;
31
+ readonly scorerName?: string | undefined;
32
+ }) => string;
33
+ //# sourceMappingURL=scorerLabel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scorerLabel.d.ts","sourceRoot":"","sources":["../../../src/internal/scorerLabel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,GACtB,UAAU;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,KAC9E,MAGF,CAAA"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * The one scorer label rule every rendering in this package shares.
3
+ *
4
+ * A scorer has two identities: `scorer` is the key, a digest of the scorer's
5
+ * own declaration and the only thing a baseline matches on, and `scorerName`
6
+ * is the readable name carried beside it. A digest is unreadable in a report
7
+ * cell, a CI log line, or a diagnostic, so each of them prints the name with
8
+ * the first eight key characters instead. Without a name there is nothing to
9
+ * abbreviate the key down to, so the label is the key itself: a truncated
10
+ * digest and no name would leave a reader nothing to match a baseline record
11
+ * on. One rule is what keeps a report from naming one scorer two ways, an
12
+ * inconclusive reason built by the runner and a Missing row built by the
13
+ * reporter agree.
14
+ *
15
+ * @since 0.1.0
16
+ */
17
+ /**
18
+ * Renders `name (first 8 of the key)`, or the bare key when the scorer carries
19
+ * no name.
20
+ *
21
+ * Every flow carries a name, so a scorer a run holds always has one. A stored
22
+ * observation may not: a baseline recorded before a scorer was named, or one
23
+ * written by another tool, carries the key alone, and it labels as the key
24
+ * rather than as a blank followed by a digest.
25
+ *
26
+ * @since 0.1.0
27
+ * @private
28
+ */
29
+ export const scorerLabel = (identity) => {
30
+ const name = identity.scorerName;
31
+ return name === undefined || name.length === 0 ? identity.scorer : `${name} (${identity.scorer.slice(0, 8)})`;
32
+ };
33
+ //# sourceMappingURL=scorerLabel.js.map