@uptimizr/db 2.0.1 → 2.1.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 (171) hide show
  1. package/AGENTS.md +228 -3
  2. package/README.md +13 -0
  3. package/dist/duckdb/migrations.d.ts.map +1 -1
  4. package/dist/duckdb/migrations.js +169 -0
  5. package/dist/duckdb/migrations.js.map +1 -1
  6. package/dist/duckdb/panelSpecs.d.ts +29 -0
  7. package/dist/duckdb/panelSpecs.d.ts.map +1 -0
  8. package/dist/duckdb/panelSpecs.js +104 -0
  9. package/dist/duckdb/panelSpecs.js.map +1 -0
  10. package/dist/duckdb/projectMetadata.d.ts +43 -0
  11. package/dist/duckdb/projectMetadata.d.ts.map +1 -0
  12. package/dist/duckdb/projectMetadata.js +222 -0
  13. package/dist/duckdb/projectMetadata.js.map +1 -0
  14. package/dist/duckdb/subscriptions.d.ts +45 -0
  15. package/dist/duckdb/subscriptions.d.ts.map +1 -0
  16. package/dist/duckdb/subscriptions.js +202 -0
  17. package/dist/duckdb/subscriptions.js.map +1 -0
  18. package/dist/index.d.ts +18 -4
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +48 -3
  21. package/dist/index.js.map +1 -1
  22. package/dist/insights/anomalies.d.ts +213 -0
  23. package/dist/insights/anomalies.d.ts.map +1 -0
  24. package/dist/insights/anomalies.js +340 -0
  25. package/dist/insights/anomalies.js.map +1 -0
  26. package/dist/insights/baseline.d.ts +51 -0
  27. package/dist/insights/baseline.d.ts.map +1 -0
  28. package/dist/insights/baseline.js +48 -0
  29. package/dist/insights/baseline.js.map +1 -0
  30. package/dist/insights/buckets.d.ts +114 -0
  31. package/dist/insights/buckets.d.ts.map +1 -0
  32. package/dist/insights/buckets.js +220 -0
  33. package/dist/insights/buckets.js.map +1 -0
  34. package/dist/insights/changepoint.d.ts +130 -0
  35. package/dist/insights/changepoint.d.ts.map +1 -0
  36. package/dist/insights/changepoint.js +209 -0
  37. package/dist/insights/changepoint.js.map +1 -0
  38. package/dist/insights/evaluate.d.ts +63 -0
  39. package/dist/insights/evaluate.d.ts.map +1 -0
  40. package/dist/insights/evaluate.js +158 -0
  41. package/dist/insights/evaluate.js.map +1 -0
  42. package/dist/insights/health.d.ts +219 -0
  43. package/dist/insights/health.d.ts.map +1 -0
  44. package/dist/insights/health.js +349 -0
  45. package/dist/insights/health.js.map +1 -0
  46. package/dist/insights/index.d.ts +45 -0
  47. package/dist/insights/index.d.ts.map +1 -0
  48. package/dist/insights/index.js +40 -0
  49. package/dist/insights/index.js.map +1 -0
  50. package/dist/insights/measures.d.ts +222 -0
  51. package/dist/insights/measures.d.ts.map +1 -0
  52. package/dist/insights/measures.js +431 -0
  53. package/dist/insights/measures.js.map +1 -0
  54. package/dist/insights/movers.d.ts +123 -0
  55. package/dist/insights/movers.d.ts.map +1 -0
  56. package/dist/insights/movers.js +166 -0
  57. package/dist/insights/movers.js.map +1 -0
  58. package/dist/insights/significance.d.ts +221 -0
  59. package/dist/insights/significance.d.ts.map +1 -0
  60. package/dist/insights/significance.js +531 -0
  61. package/dist/insights/significance.js.map +1 -0
  62. package/dist/insights/stats.d.ts +104 -0
  63. package/dist/insights/stats.d.ts.map +1 -0
  64. package/dist/insights/stats.js +181 -0
  65. package/dist/insights/stats.js.map +1 -0
  66. package/dist/insights/windows.d.ts +95 -0
  67. package/dist/insights/windows.d.ts.map +1 -0
  68. package/dist/insights/windows.js +97 -0
  69. package/dist/insights/windows.js.map +1 -0
  70. package/dist/metadata.d.ts +178 -1
  71. package/dist/metadata.d.ts.map +1 -1
  72. package/dist/metadata.js +80 -0
  73. package/dist/metadata.js.map +1 -1
  74. package/dist/narrative/build.d.ts +78 -0
  75. package/dist/narrative/build.d.ts.map +1 -0
  76. package/dist/narrative/build.js +437 -0
  77. package/dist/narrative/build.js.map +1 -0
  78. package/dist/narrative/index.d.ts +14 -0
  79. package/dist/narrative/index.d.ts.map +1 -0
  80. package/dist/narrative/index.js +13 -0
  81. package/dist/narrative/index.js.map +1 -0
  82. package/dist/narrative/text.d.ts +25 -0
  83. package/dist/narrative/text.d.ts.map +1 -0
  84. package/dist/narrative/text.js +41 -0
  85. package/dist/narrative/text.js.map +1 -0
  86. package/dist/parity/cases.d.ts +3 -0
  87. package/dist/parity/cases.d.ts.map +1 -1
  88. package/dist/parity/cases.js +387 -7
  89. package/dist/parity/cases.js.map +1 -1
  90. package/dist/parity/compare.d.ts +19 -0
  91. package/dist/parity/compare.d.ts.map +1 -1
  92. package/dist/parity/compare.js +19 -0
  93. package/dist/parity/compare.js.map +1 -1
  94. package/dist/parity/fixtures.d.ts.map +1 -1
  95. package/dist/parity/fixtures.js +25 -0
  96. package/dist/parity/fixtures.js.map +1 -1
  97. package/dist/query/aggregations.d.ts +46 -1
  98. package/dist/query/aggregations.d.ts.map +1 -1
  99. package/dist/query/aggregations.js +86 -0
  100. package/dist/query/aggregations.js.map +1 -1
  101. package/dist/query/clickhouseDialect.d.ts.map +1 -1
  102. package/dist/query/clickhouseDialect.js +5 -1
  103. package/dist/query/clickhouseDialect.js.map +1 -1
  104. package/dist/query/customEventVocabulary.d.ts +46 -0
  105. package/dist/query/customEventVocabulary.d.ts.map +1 -0
  106. package/dist/query/customEventVocabulary.js +127 -0
  107. package/dist/query/customEventVocabulary.js.map +1 -0
  108. package/dist/query/dsl/builders.d.ts +38 -0
  109. package/dist/query/dsl/builders.d.ts.map +1 -0
  110. package/dist/query/dsl/builders.js +35 -0
  111. package/dist/query/dsl/builders.js.map +1 -0
  112. package/dist/query/dsl/compare.d.ts +167 -0
  113. package/dist/query/dsl/compare.d.ts.map +1 -0
  114. package/dist/query/dsl/compare.js +358 -0
  115. package/dist/query/dsl/compare.js.map +1 -0
  116. package/dist/query/dsl/compile.d.ts +89 -0
  117. package/dist/query/dsl/compile.d.ts.map +1 -0
  118. package/dist/query/dsl/compile.js +174 -0
  119. package/dist/query/dsl/compile.js.map +1 -0
  120. package/dist/query/dsl/explain.d.ts +115 -0
  121. package/dist/query/dsl/explain.d.ts.map +1 -0
  122. package/dist/query/dsl/explain.js +143 -0
  123. package/dist/query/dsl/explain.js.map +1 -0
  124. package/dist/query/dsl/generic.d.ts +127 -0
  125. package/dist/query/dsl/generic.d.ts.map +1 -0
  126. package/dist/query/dsl/generic.js +323 -0
  127. package/dist/query/dsl/generic.js.map +1 -0
  128. package/dist/query/dsl/index.d.ts +30 -0
  129. package/dist/query/dsl/index.d.ts.map +1 -0
  130. package/dist/query/dsl/index.js +23 -0
  131. package/dist/query/dsl/index.js.map +1 -0
  132. package/dist/query/dsl/order.d.ts +41 -0
  133. package/dist/query/dsl/order.d.ts.map +1 -0
  134. package/dist/query/dsl/order.js +55 -0
  135. package/dist/query/dsl/order.js.map +1 -0
  136. package/dist/query/dsl/significance.d.ts +130 -0
  137. package/dist/query/dsl/significance.d.ts.map +1 -0
  138. package/dist/query/dsl/significance.js +224 -0
  139. package/dist/query/dsl/significance.js.map +1 -0
  140. package/dist/query/index.d.ts +2 -0
  141. package/dist/query/index.d.ts.map +1 -1
  142. package/dist/query/index.js +8 -0
  143. package/dist/query/index.js.map +1 -1
  144. package/dist/query/summary/index.d.ts +4 -2
  145. package/dist/query/summary/index.d.ts.map +1 -1
  146. package/dist/query/summary/index.js +2 -1
  147. package/dist/query/summary/index.js.map +1 -1
  148. package/dist/query/summary/labels.d.ts +101 -0
  149. package/dist/query/summary/labels.d.ts.map +1 -0
  150. package/dist/query/summary/labels.js +261 -0
  151. package/dist/query/summary/labels.js.map +1 -0
  152. package/dist/query/summary/reading.d.ts.map +1 -1
  153. package/dist/query/summary/reading.js +21 -2
  154. package/dist/query/summary/reading.js.map +1 -1
  155. package/dist/query/summary/schema.d.ts +250 -20
  156. package/dist/query/summary/schema.d.ts.map +1 -1
  157. package/dist/query/summary/schema.js +135 -123
  158. package/dist/query/summary/schema.js.map +1 -1
  159. package/dist/query/summary/summarize.d.ts.map +1 -1
  160. package/dist/query/summary/summarize.js +91 -5
  161. package/dist/query/summary/summarize.js.map +1 -1
  162. package/dist/query/summary/types.d.ts +77 -2
  163. package/dist/query/summary/types.d.ts.map +1 -1
  164. package/dist/query/types.d.ts +47 -1
  165. package/dist/query/types.d.ts.map +1 -1
  166. package/dist/subscriptions.d.ts +208 -0
  167. package/dist/subscriptions.d.ts.map +1 -0
  168. package/dist/subscriptions.js +153 -0
  169. package/dist/subscriptions.js.map +1 -0
  170. package/llms.txt +9 -0
  171. package/package.json +8 -8
@@ -0,0 +1,323 @@
1
+ /**
2
+ * **The generic group-by compiler** (ADR 0051 §3, design sketch §C.2, tier 2).
3
+ *
4
+ * The delegated tier (`compile.ts`) can run any metric, but only at the grain
5
+ * its builder renders: `top_meshes` is one row per mesh and nothing else. That
6
+ * is right for a spatial binning or a percentile — those measures *are* their
7
+ * grain — but wrong for a count. "How many mesh interactions per input source"
8
+ * and "…per scene" are the same question about the same events, and the only
9
+ * reason they needed two endpoints is that somebody wrote two `GROUP BY`s.
10
+ *
11
+ * So a metric that declares `genericGroupBy` in the registry (a portable
12
+ * `count` / `count(DISTINCT session_id)` / `sum` / `avg` / `max` over promoted
13
+ * columns) gets one shared, dialect-authored builder that renders
14
+ *
15
+ * ```sql
16
+ * SELECT <dimensions>, <measures>
17
+ * FROM events [LEFT JOIN session_attrs …]
18
+ * WHERE project_id = ? AND <event types> AND <scope> AND <range> AND <filters>
19
+ * GROUP BY <dimensions>
20
+ * ORDER BY <measure> DESC, <dimensions>
21
+ * LIMIT ?
22
+ * ```
23
+ *
24
+ * for **any** subset of the dimensions that metric declares.
25
+ *
26
+ * ## Why this is still only one SQL path
27
+ *
28
+ * Everything variable in the SQL above comes from **registry data**, never from
29
+ * the request: the event types and the scope predicate are the metric's
30
+ * `genericGroupBy`, the measures are its declared columns, and each dimension's
31
+ * expression is chosen from a closed table keyed by `DimensionId`. Every value a
32
+ * caller supplies — a scene id, a mesh name, an event predicate — reaches the
33
+ * SQL as a bound parameter through the same {@link ParamBag} the canned
34
+ * aggregations use. There is no string interpolation of caller input anywhere in
35
+ * this file, which is what makes a grammar with a `GROUP BY` in it still a
36
+ * closed grammar.
37
+ *
38
+ * ## Portability
39
+ *
40
+ * The promoted dimensions (`scene_id`, `session_id`, `mesh`, `name`, `source`,
41
+ * `event_type`) are plain columns on every engine. The session attributes
42
+ * (`cameraMode`, `device.*`) live in the `session_start` payload, so they are
43
+ * read through `Dialect.jsonText` in one CTE and `LEFT JOIN`ed on `session_id`
44
+ * — the same shape `buildPerfByDevice` has always used. `GROUP BY` repeats the
45
+ * expressions rather than naming the output aliases, because T-SQL rejects the
46
+ * latter (the mssql dialect rewrites it, but not relying on the rewrite is
47
+ * cheaper than relying on it).
48
+ *
49
+ * ## Why it is not called `build*`
50
+ *
51
+ * Every `build*` export of this package is a dialect-agnostic aggregation with
52
+ * the signature `(projectId, opts, dialect)`, and the optional stores' suites
53
+ * enumerate them by that prefix to run each one against DuckDB. This one takes a
54
+ * `MetricDefinition` first, because the metric *is* its configuration — so it is
55
+ * a `compile*`, next to `compileMetric` and `compileQuery`, and the prefix keeps
56
+ * meaning what it has always meant.
57
+ *
58
+ * The one session attribute deliberately left out is `device.isMobile`: it is a
59
+ * boolean, and a group-by on it would key rows by `"true"` on one engine and `1`
60
+ * on another. `GENERIC_DIMENSIONS` in the registry says so, and `validateQuery`
61
+ * refuses it by name.
62
+ */
63
+ import { dimensionColumn, } from "@uptimizr/metrics";
64
+ import { ParamBag, cameraModeClause, rangeClause } from "../dialect.js";
65
+ /** The default row cap when a caller names none. */
66
+ const DEFAULT_GENERIC_LIMIT = 200;
67
+ /** Session attributes read out of the `session_start` payload, by dimension. */
68
+ const SESSION_ATTRIBUTE_PATHS = {
69
+ cameraMode: ["scene", "cameraType"],
70
+ "device.engine": ["device", "engine"],
71
+ "device.renderer": ["device", "renderer"],
72
+ "device.browser": ["device", "browser"],
73
+ "device.os": ["device", "os"],
74
+ };
75
+ /** Promoted `events` columns, by dimension. The rest come from the CTE. */
76
+ const PROMOTED_COLUMNS = {
77
+ scene: "scene_id",
78
+ session: "session_id",
79
+ mesh: "mesh",
80
+ name: "name",
81
+ source: "source",
82
+ event_type: "event_type",
83
+ };
84
+ /** The CTE the session-attribute dimensions are read from. */
85
+ const SESSION_CTE = "session_attrs";
86
+ /** Whether a dimension is read from the joined `session_start` attributes. */
87
+ function isSessionAttribute(dimension) {
88
+ return dimension in SESSION_ATTRIBUTE_PATHS;
89
+ }
90
+ /** Sanitised alias for a session attribute inside the CTE (`device.os` → `sa_os`). */
91
+ function attributeAlias(dimension) {
92
+ return `sa_${dimension
93
+ .replace(/^device\./, "")
94
+ .replace(/[^A-Za-z0-9]/g, "_")
95
+ .toLowerCase()}`;
96
+ }
97
+ /** Resolve every asked-for dimension to its output column and SQL expression. */
98
+ function resolveDimensions(metric, dimensions) {
99
+ return dimensions.map((id) => ({
100
+ id,
101
+ column: dimensionColumn(metric, id),
102
+ expression: isSessionAttribute(id)
103
+ ? `${SESSION_CTE}.${attributeAlias(id)}`
104
+ : `events.${PROMOTED_COLUMNS[id] ?? id}`,
105
+ }));
106
+ }
107
+ /** Render one measure column. */
108
+ function renderMeasure(measure) {
109
+ switch (measure.kind) {
110
+ case "count":
111
+ return `count(*) AS ${measure.column}`;
112
+ case "sessions":
113
+ return `count(DISTINCT events.session_id) AS ${measure.column}`;
114
+ case "sum":
115
+ return `sum(events.${measure.of}) AS ${measure.column}`;
116
+ case "avg":
117
+ return `avg(events.${measure.of}) AS ${measure.column}`;
118
+ case "max":
119
+ return `max(events.${measure.of}) AS ${measure.column}`;
120
+ }
121
+ }
122
+ /**
123
+ * `event_type IN (…)`, every type a bound parameter. Empty for a metric whose
124
+ * subject is the whole stream (`event_counts`).
125
+ */
126
+ function eventTypeClause(bag, types) {
127
+ if (types.length === 0)
128
+ return "";
129
+ const bound = types.map((type, index) => bag.add(`gType${index}`, "string", type));
130
+ return ` AND events.event_type IN (${bound.join(", ")})`;
131
+ }
132
+ /**
133
+ * The metric's own scope predicate, so a regrouped result counts the same
134
+ * population its builder counts. Closed: each value is one non-empty test on a
135
+ * promoted column, named by the registry, never by the caller.
136
+ */
137
+ function scopeClause(scope) {
138
+ const parts = (scope ?? []).map((rule) => rule === "hasMesh" ? "events.mesh != ''" : "events.name != ''");
139
+ return parts.length === 0 ? "" : ` AND ${parts.join(" AND ")}`;
140
+ }
141
+ /** An equality predicate on a promoted column, or `""` when the value is absent. */
142
+ function equality(bag, column, param, value) {
143
+ if (value == null || value.length === 0)
144
+ return "";
145
+ return ` AND events.${column} = ${bag.add(param, "string", value)}`;
146
+ }
147
+ /**
148
+ * `session_id IN (SELECT … FROM session_start WHERE <attribute> = ?)` — the
149
+ * device filter, rendered exactly like the camera-mode filter it sits next to so
150
+ * it composes with every other clause and needs no join.
151
+ */
152
+ function deviceClause(bag, d, projectId, device) {
153
+ const tests = [];
154
+ if (device?.os != null && device.os.length > 0) {
155
+ tests.push(`${d.jsonText("payload", "device", "os")} = ${bag.add("devOs", "string", device.os)}`);
156
+ }
157
+ if (device?.browser != null && device.browser.length > 0) {
158
+ tests.push(`${d.jsonText("payload", "device", "browser")} = ` +
159
+ `${bag.add("devBrowser", "string", device.browser)}`);
160
+ }
161
+ if (tests.length === 0)
162
+ return "";
163
+ const pid = bag.add("devProjectId", "string", projectId);
164
+ return ` AND events.session_id IN (
165
+ SELECT session_id FROM events
166
+ WHERE project_id = ${pid} AND event_type = 'session_start' AND ${tests.join(" AND ")}
167
+ )`;
168
+ }
169
+ /**
170
+ * `session_id IN (SELECT … WHERE <the step predicate>)` — the ADR 0038 event
171
+ * filter.
172
+ *
173
+ * It is a **cohort** predicate, not a row predicate: it keeps the sessions in
174
+ * which the event happened at least once, and the metric then counts its own
175
+ * events within them. That is the useful question ("what did the people who
176
+ * reached checkout look at") and the one the funnel grammar already answers;
177
+ * narrowing the counted rows themselves is what `filters.event_type`-style
178
+ * dimensions and `eventTypes` already do.
179
+ */
180
+ function eventPredicateClause(bag, projectId, event) {
181
+ if (event == null)
182
+ return "";
183
+ const parts = [`event_type = ${bag.add("evType", "string", event.type)}`];
184
+ if (event.name != null && event.name.length > 0) {
185
+ parts.push(`name = ${bag.add("evName", "string", event.name)}`);
186
+ }
187
+ if (event.mesh != null && event.mesh.length > 0) {
188
+ parts.push(`mesh = ${bag.add("evMesh", "string", event.mesh)}`);
189
+ }
190
+ const pid = bag.add("evProjectId", "string", projectId);
191
+ return ` AND events.session_id IN (
192
+ SELECT session_id FROM events
193
+ WHERE project_id = ${pid} AND ${parts.join(" AND ")}
194
+ )`;
195
+ }
196
+ /**
197
+ * Equality predicates for the dimensions a `segment` holds fixed. Only the
198
+ * dimensions the delegated filters cannot express reach here — the rest arrive
199
+ * as ordinary filter options.
200
+ */
201
+ function segmentClauses(bag, d, projectId, segment) {
202
+ if (segment == null)
203
+ return "";
204
+ const parts = [];
205
+ let index = 0;
206
+ for (const [dimension, value] of Object.entries(segment)) {
207
+ if (value == null || value.length === 0)
208
+ continue;
209
+ const param = `seg${index++}`;
210
+ const path = SESSION_ATTRIBUTE_PATHS[dimension];
211
+ if (path != null) {
212
+ const pid = bag.add(`${param}ProjectId`, "string", projectId);
213
+ parts.push(`events.session_id IN (
214
+ SELECT session_id FROM events
215
+ WHERE project_id = ${pid} AND event_type = 'session_start'
216
+ AND ${d.jsonText("payload", ...path)} = ${bag.add(param, "string", value)}
217
+ )`);
218
+ continue;
219
+ }
220
+ const column = PROMOTED_COLUMNS[dimension];
221
+ if (column == null)
222
+ continue;
223
+ parts.push(`events.${column} = ${bag.add(param, "string", value)}`);
224
+ }
225
+ return parts.length === 0 ? "" : ` AND ${parts.join(" AND ")}`;
226
+ }
227
+ /**
228
+ * The `session_start` attribute CTE and its join, rendered only when a session
229
+ * attribute is actually one of the grouping dimensions. A device or camera-mode
230
+ * *filter* never needs it — those are sub-selects.
231
+ */
232
+ function sessionAttributes(bag, d, projectId, dimensions) {
233
+ const attributes = dimensions.filter((dimension) => isSessionAttribute(dimension.id));
234
+ if (attributes.length === 0)
235
+ return { cte: "", join: "" };
236
+ const pid = bag.add("saProjectId", "string", projectId);
237
+ const projections = attributes.map((dimension) => `${d.jsonText("payload", ...(SESSION_ATTRIBUTE_PATHS[dimension.id] ?? []))} AS ` +
238
+ `${attributeAlias(dimension.id)}`);
239
+ return {
240
+ cte: `WITH ${SESSION_CTE} AS (
241
+ SELECT session_id AS sa_session, ${projections.join(", ")}
242
+ FROM events
243
+ WHERE project_id = ${pid} AND event_type = 'session_start'
244
+ )
245
+ `,
246
+ join: `
247
+ LEFT JOIN ${SESSION_CTE} ON ${SESSION_CTE}.sa_session = events.session_id`,
248
+ };
249
+ }
250
+ /**
251
+ * Compile a generic group-by query for one metric.
252
+ *
253
+ * `metric` must declare `genericGroupBy` — the caller (`compileMetric`) has
254
+ * already checked that, and `validateQuery` before it, so the throw below guards
255
+ * a programming error rather than a client one.
256
+ */
257
+ export function compileGenericGroupBy(metric, projectId, opts, d) {
258
+ const spec = metric.genericGroupBy;
259
+ if (spec == null) {
260
+ throw new Error(`metric '${metric.id}' declares no genericGroupBy and cannot be grouped by an arbitrary grain`);
261
+ }
262
+ const bag = new ParamBag(d);
263
+ const dimensions = resolveDimensions(metric, opts.dimensions ?? metric.grainDimensions);
264
+ // The attribute CTE binds its own project id, and must be rendered before the
265
+ // outer clauses so the parameters read in source order.
266
+ const attributes = sessionAttributes(bag, d, projectId, dimensions);
267
+ const pid = bag.add("projectId", "string", projectId);
268
+ const where = [
269
+ eventTypeClause(bag, spec.eventTypes),
270
+ scopeClause(spec.scope),
271
+ rangeClause(bag, opts),
272
+ equality(bag, "scene_id", "scene", opts.scene),
273
+ equality(bag, "session_id", "session", opts.session),
274
+ equality(bag, "source", "source", opts.source),
275
+ equality(bag, "mesh", "mesh", opts.mesh),
276
+ cameraModeClause(bag, d, projectId, opts),
277
+ deviceClause(bag, d, projectId, opts.device),
278
+ eventPredicateClause(bag, projectId, opts.event),
279
+ segmentClauses(bag, d, projectId, opts.segment),
280
+ ].join("");
281
+ const measures = spec.measures.map(renderMeasure);
282
+ const selected = [
283
+ ...dimensions.map((dimension) => `${dimension.expression} AS ${dimension.column}`),
284
+ ...measures,
285
+ ];
286
+ // Order by the asked-for measure, then by every dimension, so the row set a
287
+ // `LIMIT` keeps is a function of the data rather than of the engine's
288
+ // scan order — the same reason the summariser breaks ties on the label.
289
+ const primary = opts.order?.by ?? spec.measures[0]?.column ?? "1";
290
+ const direction = opts.order?.dir === "asc" ? "ASC" : "DESC";
291
+ const order = [
292
+ `${primary} ${direction}`,
293
+ ...dimensions.map((dimension) => `${dimension.expression} ASC`),
294
+ ].join(", ");
295
+ const limit = bag.add("limit", "u32", opts.limit ?? DEFAULT_GENERIC_LIMIT);
296
+ const groupBy = dimensions.length === 0
297
+ ? ""
298
+ : `
299
+ GROUP BY ${dimensions.map((dimension) => dimension.expression).join(", ")}`;
300
+ return {
301
+ query: `
302
+ ${attributes.cte}SELECT ${selected.join(", ")}
303
+ FROM events${attributes.join}
304
+ WHERE events.project_id = ${pid}${where}${groupBy}
305
+ ORDER BY ${order}
306
+ LIMIT ${limit}
307
+ `,
308
+ metric: metric.id,
309
+ query_params: bag.values,
310
+ };
311
+ }
312
+ /**
313
+ * The columns a generic group-by result carries, in order: the grouping
314
+ * dimensions, then the metric's measures. Used by the explain plan and by the
315
+ * tests that assert a generic row is self-describing.
316
+ */
317
+ export function genericResultColumns(metric, dimensions) {
318
+ return [
319
+ ...dimensions.map((dimension) => dimensionColumn(metric, dimension)),
320
+ ...(metric.genericGroupBy?.measures ?? []).map((measure) => measure.column),
321
+ ];
322
+ }
323
+ //# sourceMappingURL=generic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generic.js","sourceRoot":"","sources":["../../../src/query/dsl/generic.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * The query DSL's compilation tiers (ADR 0051 §3, design sketch §C.2), and the
3
+ * four pure layers that sit on top of them.
4
+ *
5
+ * - **delegated** (`compile.ts`) — a validated `queryV1` document is mapped onto
6
+ * the metric's existing aggregation builder, so every metric is reachable at
7
+ * exactly the power its canned endpoint has.
8
+ * - **generic** (`generic.ts`) — a metric that declares `genericGroupBy` is
9
+ * recomputed at any grain it declares, by one shared dialect-authored builder.
10
+ *
11
+ * On top: `compare.ts` joins two runs of the same spec, `significance.ts` says
12
+ * whether the difference is real, `explain.ts` renders the plan and the reasons
13
+ * not to trust it, and `order.ts` re-sorts a delegated result honestly. All four
14
+ * are pure — rows in, rows out — and none of them runs a query.
15
+ */
16
+ export { builderFor } from "./builders.js";
17
+ export type { AggregationBuilder } from "./builders.js";
18
+ export { cameraTypeForMode, compileMetric, compileQuery, toBuilderOptions } from "./compile.js";
19
+ export type { MetricQueryOptions, QueryResolution } from "./compile.js";
20
+ export { compileGenericGroupBy, genericResultColumns } from "./generic.js";
21
+ export type { GenericDeviceFilter, GenericEventPredicate, GenericQueryOptions } from "./generic.js";
22
+ export { compareRows, comparisonKeys, summarizeComparison } from "./compare.js";
23
+ export type { ComparisonBasis, ComparisonContext, ComparisonMeta, ComparisonResult, ComparisonRow, ComparisonSide, MoversSummary, } from "./compare.js";
24
+ export { DEFAULT_ALPHA, normalCdf, sampleOf, studentTTwoSided, twoProportionZ, welchT, wilsonScoreInterval, } from "./significance.js";
25
+ export type { MeanSignificance, Proportion, ProportionSignificance, Sample, ScoreInterval, Significance, } from "./significance.js";
26
+ export { channelRows, explainQuery, explainSpec, planWarnings, silentChannels } from "./explain.js";
27
+ export type { ExplainParam, ExplainParamType, PlanContext, QueryPlan } from "./explain.js";
28
+ export { ORDER_AFTER_CAP_CAVEAT, applyOrder, reordersCappedResult } from "./order.js";
29
+ export type { ResultOrder } from "./order.js";
30
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/query/dsl/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,YAAY,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChG,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACxE,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAC3E,YAAY,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACpG,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAChF,YAAY,EACV,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,aAAa,EACb,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,MAAM,EACN,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,gBAAgB,EAChB,UAAU,EACV,sBAAsB,EACtB,MAAM,EACN,aAAa,EACb,YAAY,GACb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACpG,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAC3F,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AACtF,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * The query DSL's compilation tiers (ADR 0051 §3, design sketch §C.2), and the
3
+ * four pure layers that sit on top of them.
4
+ *
5
+ * - **delegated** (`compile.ts`) — a validated `queryV1` document is mapped onto
6
+ * the metric's existing aggregation builder, so every metric is reachable at
7
+ * exactly the power its canned endpoint has.
8
+ * - **generic** (`generic.ts`) — a metric that declares `genericGroupBy` is
9
+ * recomputed at any grain it declares, by one shared dialect-authored builder.
10
+ *
11
+ * On top: `compare.ts` joins two runs of the same spec, `significance.ts` says
12
+ * whether the difference is real, `explain.ts` renders the plan and the reasons
13
+ * not to trust it, and `order.ts` re-sorts a delegated result honestly. All four
14
+ * are pure — rows in, rows out — and none of them runs a query.
15
+ */
16
+ export { builderFor } from "./builders.js";
17
+ export { cameraTypeForMode, compileMetric, compileQuery, toBuilderOptions } from "./compile.js";
18
+ export { compileGenericGroupBy, genericResultColumns } from "./generic.js";
19
+ export { compareRows, comparisonKeys, summarizeComparison } from "./compare.js";
20
+ export { DEFAULT_ALPHA, normalCdf, sampleOf, studentTTwoSided, twoProportionZ, welchT, wilsonScoreInterval, } from "./significance.js";
21
+ export { channelRows, explainQuery, explainSpec, planWarnings, silentChannels } from "./explain.js";
22
+ export { ORDER_AFTER_CAP_CAVEAT, applyOrder, reordersCappedResult } from "./order.js";
23
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/query/dsl/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEhG,OAAO,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAUhF,OAAO,EACL,aAAa,EACb,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,MAAM,EACN,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAS3B,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEpG,OAAO,EAAE,sBAAsB,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * **`order`: on the delegated tier** (ADR 0051 §3).
3
+ *
4
+ * The generic builder puts the caller's `order` straight into its `ORDER BY`, so
5
+ * the row cap keeps the rows the caller asked for. A *delegated* metric cannot:
6
+ * its builder has one `ORDER BY` of its own, and it applied the `LIMIT` in that
7
+ * order before these rows existed.
8
+ *
9
+ * So a delegated re-order is exactly what it looks like — a re-sort of the rows
10
+ * that came back, done here rather than in the model's head. What it is **not**
11
+ * is a "bottom N": asking `top_meshes` for `count ASC` returns the *top* rows
12
+ * sorted ascending, because the store already discarded everything below the
13
+ * cap. {@link ORDER_AFTER_CAP_CAVEAT} says so, and the collector attaches it to
14
+ * the envelope whenever this function actually changed the order of a capped
15
+ * result — which is the only honest way to offer the feature at all.
16
+ */
17
+ /** A row as it leaves a store. */
18
+ type Row = Readonly<Record<string, unknown>>;
19
+ /** What `order` asks for. */
20
+ export interface ResultOrder {
21
+ by: string;
22
+ dir: "asc" | "desc";
23
+ }
24
+ /** Attached to a result whose delegated rows were re-ordered after the cap. */
25
+ export declare const ORDER_AFTER_CAP_CAVEAT: string;
26
+ /**
27
+ * Re-sort rows by a column, missing and non-numeric values last in either
28
+ * direction (a row with no value is not "the smallest"; it is unmeasured).
29
+ * Ties break on the remaining columns' rendered values so the order is a
30
+ * function of the row set rather than of the engine's scan order.
31
+ *
32
+ * Returns a new array; the input is never mutated.
33
+ */
34
+ export declare function applyOrder<T extends Row>(rows: readonly T[], order: ResultOrder): T[];
35
+ /**
36
+ * Whether re-ordering `rows` could misrepresent a capped result — the condition
37
+ * under which {@link ORDER_AFTER_CAP_CAVEAT} belongs on the envelope.
38
+ */
39
+ export declare function reordersCappedResult(rows: readonly Row[], limit: number | undefined): boolean;
40
+ export {};
41
+ //# sourceMappingURL=order.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order.d.ts","sourceRoot":"","sources":["../../../src/query/dsl/order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,kCAAkC;AAClC,KAAK,GAAG,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAE7C,6BAA6B;AAC7B,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,KAAK,GAAG,MAAM,CAAC;CACrB;AAED,+EAA+E;AAC/E,eAAO,MAAM,sBAAsB,QAGiD,CAAC;AAErF;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,CAAC,SAAS,GAAG,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,GAAG,CAAC,EAAE,CAgBrF;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAE7F"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * **`order`: on the delegated tier** (ADR 0051 §3).
3
+ *
4
+ * The generic builder puts the caller's `order` straight into its `ORDER BY`, so
5
+ * the row cap keeps the rows the caller asked for. A *delegated* metric cannot:
6
+ * its builder has one `ORDER BY` of its own, and it applied the `LIMIT` in that
7
+ * order before these rows existed.
8
+ *
9
+ * So a delegated re-order is exactly what it looks like — a re-sort of the rows
10
+ * that came back, done here rather than in the model's head. What it is **not**
11
+ * is a "bottom N": asking `top_meshes` for `count ASC` returns the *top* rows
12
+ * sorted ascending, because the store already discarded everything below the
13
+ * cap. {@link ORDER_AFTER_CAP_CAVEAT} says so, and the collector attaches it to
14
+ * the envelope whenever this function actually changed the order of a capped
15
+ * result — which is the only honest way to offer the feature at all.
16
+ */
17
+ /** Attached to a result whose delegated rows were re-ordered after the cap. */
18
+ export const ORDER_AFTER_CAP_CAVEAT = "`order` was applied to the rows this metric returned, which its own builder had already " +
19
+ "capped in its own order — so an ascending sort shows the smallest of the top rows, not the " +
20
+ "smallest overall. Raise `limit`, or group by the dimension you want the tail of.";
21
+ /**
22
+ * Re-sort rows by a column, missing and non-numeric values last in either
23
+ * direction (a row with no value is not "the smallest"; it is unmeasured).
24
+ * Ties break on the remaining columns' rendered values so the order is a
25
+ * function of the row set rather than of the engine's scan order.
26
+ *
27
+ * Returns a new array; the input is never mutated.
28
+ */
29
+ export function applyOrder(rows, order) {
30
+ const sign = order.dir === "asc" ? 1 : -1;
31
+ return [...rows].sort((a, b) => {
32
+ const left = a[order.by];
33
+ const right = b[order.by];
34
+ const leftNumber = typeof left === "number" && Number.isFinite(left) ? left : null;
35
+ const rightNumber = typeof right === "number" && Number.isFinite(right) ? right : null;
36
+ if (leftNumber == null || rightNumber == null) {
37
+ if (leftNumber !== rightNumber)
38
+ return leftNumber == null ? 1 : -1;
39
+ }
40
+ else if (leftNumber !== rightNumber) {
41
+ return sign * (leftNumber - rightNumber);
42
+ }
43
+ const leftKey = JSON.stringify(a);
44
+ const rightKey = JSON.stringify(b);
45
+ return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
46
+ });
47
+ }
48
+ /**
49
+ * Whether re-ordering `rows` could misrepresent a capped result — the condition
50
+ * under which {@link ORDER_AFTER_CAP_CAVEAT} belongs on the envelope.
51
+ */
52
+ export function reordersCappedResult(rows, limit) {
53
+ return limit != null && Number.isFinite(limit) && rows.length >= limit;
54
+ }
55
+ //# sourceMappingURL=order.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"order.js","sourceRoot":"","sources":["../../../src/query/dsl/order.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAWH,+EAA+E;AAC/E,MAAM,CAAC,MAAM,sBAAsB,GACjC,0FAA0F;IAC1F,6FAA6F;IAC7F,kFAAkF,CAAC;AAErF;;;;;;;GAOG;AACH,MAAM,UAAU,UAAU,CAAgB,IAAkB,EAAE,KAAkB;IAC9E,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1C,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACzB,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAC1B,MAAM,UAAU,GAAG,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QACnF,MAAM,WAAW,GAAG,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC;QACvF,IAAI,UAAU,IAAI,IAAI,IAAI,WAAW,IAAI,IAAI,EAAE,CAAC;YAC9C,IAAI,UAAU,KAAK,WAAW;gBAAE,OAAO,UAAU,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;aAAM,IAAI,UAAU,KAAK,WAAW,EAAE,CAAC;YACtC,OAAO,IAAI,GAAG,CAAC,UAAU,GAAG,WAAW,CAAC,CAAC;QAC3C,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACnC,OAAO,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAoB,EAAE,KAAyB;IAClF,OAAO,KAAK,IAAI,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,KAAK,CAAC;AACzE,CAAC"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * **Is this change real?** — the two significance tests `compare` can justify
3
+ * (ADR 0051 §4, design sketch §C.2).
4
+ *
5
+ * Pure functions over numbers: no registry, no store, no I/O. They exist here
6
+ * rather than inline in `compare.ts` so they can be unit-tested against
7
+ * textbook worked examples, which is the only way to be sure a statistic is
8
+ * right — a plausible-looking number is exactly the failure mode this file is
9
+ * meant to prevent, since a model will quote whatever it is handed.
10
+ *
11
+ * Two tests, because analytics produces two shapes of measure:
12
+ *
13
+ * - {@link twoProportionZ} — a **count or rate**. "Was 40/1000 this week
14
+ * different from 25/900 last week?" Pooled two-proportion z, two-sided normal
15
+ * p-value, plus a Wilson score interval on each proportion (which, unlike the
16
+ * Wald interval, stays inside 0..1 at the small counts a long tail produces —
17
+ * the same reason the summariser already uses it).
18
+ * - {@link welchT} — a **mean** over two samples of unequal size and variance.
19
+ * Welch's unequal-variance t-test with the Welch–Satterthwaite degrees of
20
+ * freedom. It applies where the two ranges really do give two *samples*: a
21
+ * `bucket`-grain metric compared range-over-range hands us one value per
22
+ * bucket on each side.
23
+ *
24
+ * Neither is applied speculatively. `compare.ts` computes a significance only
25
+ * when the registry says the measure supports it (`unit: count | sessions`, or a
26
+ * `rateOf` denominator) and the denominator clears the metric's
27
+ * `comparable.minSample`; otherwise the field is simply absent, because
28
+ * "no p-value" is a far better answer than a p-value computed from a
29
+ * denominator that cannot support one.
30
+ *
31
+ * ## Accuracy
32
+ *
33
+ * The normal CDF uses the Abramowitz & Stegun 7.1.26 rational approximation of
34
+ * `erf` (|error| < 1.5e-7). The Student-t CDF uses the regularised incomplete
35
+ * beta function via the Lentz continued fraction (Numerical Recipes §6.4),
36
+ * converged to 3e-16 or 300 iterations. Both are far tighter than the third
37
+ * significant figure anyone reads off a p-value.
38
+ */
39
+ /** A 95% (by default) score interval on a proportion. */
40
+ export interface ScoreInterval {
41
+ low: number;
42
+ high: number;
43
+ }
44
+ /** One side of a proportion comparison. */
45
+ export interface Proportion {
46
+ /** Events (or sessions) in this group. */
47
+ successes: number;
48
+ /** Events (or sessions) in the population the group is part of. */
49
+ trials: number;
50
+ }
51
+ /** The result of a two-proportion z test. */
52
+ export interface ProportionSignificance {
53
+ test: "two-proportion-z";
54
+ /** Share of the current population. */
55
+ current: number;
56
+ /** Share of the previous population. */
57
+ previous: number;
58
+ /** `current - previous`, in share points. */
59
+ diff: number;
60
+ /** The test statistic. */
61
+ z: number;
62
+ /** Two-sided p-value under the normal approximation. */
63
+ pValue: number;
64
+ /** Whether `pValue` clears the level the caller asked for. */
65
+ significant: boolean;
66
+ /** 95% Wilson score interval on the current share. */
67
+ currentInterval: ScoreInterval;
68
+ /** 95% Wilson score interval on the previous share. */
69
+ previousInterval: ScoreInterval;
70
+ }
71
+ /** The result of Welch's unequal-variance t test. */
72
+ export interface MeanSignificance {
73
+ test: "welch-t";
74
+ current: number;
75
+ previous: number;
76
+ diff: number;
77
+ /** The test statistic. */
78
+ t: number;
79
+ /** Welch–Satterthwaite degrees of freedom. */
80
+ df: number;
81
+ pValue: number;
82
+ significant: boolean;
83
+ /** Samples behind each mean — a t-test on two points is not evidence. */
84
+ currentSamples: number;
85
+ previousSamples: number;
86
+ }
87
+ /** Either shape of significance a comparison row can carry. */
88
+ export type Significance = ProportionSignificance | MeanSignificance;
89
+ /** The default two-sided level. */
90
+ export declare const DEFAULT_ALPHA = 0.05;
91
+ /** The standard normal CDF, Φ(x). */
92
+ export declare function normalCdf(x: number): number;
93
+ /** The two-sided p-value of a Student-t statistic with `df` degrees of freedom. */
94
+ export declare function studentTTwoSided(t: number, df: number): number;
95
+ /**
96
+ * A Wilson score interval on `successes / trials`.
97
+ *
98
+ * The same interval the summary envelope reports, restated here so this module
99
+ * stays free of the summariser (and vice versa): both need it, neither should
100
+ * import the other.
101
+ */
102
+ export declare function wilsonScoreInterval(successes: number, trials: number, z?: number): ScoreInterval;
103
+ /**
104
+ * Pooled two-proportion z test: is `current.successes / current.trials`
105
+ * different from `previous.successes / previous.trials`?
106
+ *
107
+ * Returns `null` when either side has no trials — a proportion of nothing is not
108
+ * a proportion, and reporting `z = 0` for it would read as "no change" when the
109
+ * truth is "no data".
110
+ */
111
+ export declare function twoProportionZ(current: Proportion, previous: Proportion, alpha?: number): ProportionSignificance | null;
112
+ /** One sample, described by the three numbers Welch's test needs. */
113
+ export interface Sample {
114
+ mean: number;
115
+ /** Unbiased (n − 1) sample variance. */
116
+ variance: number;
117
+ /** Number of observations. */
118
+ n: number;
119
+ }
120
+ /** Mean and unbiased variance of a list of observations; `null` below two. */
121
+ export declare function sampleOf(values: readonly number[]): Sample | null;
122
+ /**
123
+ * Welch's unequal-variance t test with the Welch–Satterthwaite degrees of
124
+ * freedom.
125
+ *
126
+ * Returns `null` when either sample has fewer than two observations (there is no
127
+ * variance to test) or when both variances are zero and the means are equal.
128
+ */
129
+ export declare function welchT(current: Sample, previous: Sample, alpha?: number): MeanSignificance | null;
130
+ //# sourceMappingURL=significance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"significance.d.ts","sourceRoot":"","sources":["../../../src/query/dsl/significance.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,yDAAyD;AACzD,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,2CAA2C;AAC3C,MAAM,WAAW,UAAU;IACzB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,mEAAmE;IACnE,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,6CAA6C;AAC7C,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,kBAAkB,CAAC;IACzB,uCAAuC;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,wDAAwD;IACxD,MAAM,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,WAAW,EAAE,OAAO,CAAC;IACrB,sDAAsD;IACtD,eAAe,EAAE,aAAa,CAAC;IAC/B,uDAAuD;IACvD,gBAAgB,EAAE,aAAa,CAAC;CACjC;AAED,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,0BAA0B;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,8CAA8C;IAC9C,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,yEAAyE;IACzE,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,+DAA+D;AAC/D,MAAM,MAAM,YAAY,GAAG,sBAAsB,GAAG,gBAAgB,CAAC;AAErE,mCAAmC;AACnC,eAAO,MAAM,aAAa,OAAO,CAAC;AAmBlC,qCAAqC;AACrC,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE3C;AAiED,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,CAAC,GAAE,MAAa,GACf,aAAa,CAUf;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,UAAU,EACnB,QAAQ,EAAE,UAAU,EACpB,KAAK,GAAE,MAAsB,GAC5B,sBAAsB,GAAG,IAAI,CAwB/B;AAED,qEAAqE;AACrE,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,8BAA8B;IAC9B,CAAC,EAAE,MAAM,CAAC;CACX;AAED,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI,CAOjE;AAED;;;;;;GAMG;AACH,wBAAgB,MAAM,CACpB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,KAAK,GAAE,MAAsB,GAC5B,gBAAgB,GAAG,IAAI,CAqBzB"}